/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.trinidadinternal.renderkit.core.xhtml;
import java.io.IOException;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.model.SelectItem;
import org.apache.myfaces.trinidad.bean.FacesBean;
import org.apache.myfaces.trinidad.bean.PropertyKey;
import org.apache.myfaces.trinidad.component.core.input.CoreSelectManyCheckbox;
import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
import org.apache.myfaces.trinidad.context.RenderingContext;
import org.apache.myfaces.trinidad.util.IntegerUtils;
/**
*/
public class SimpleSelectManyCheckboxRenderer extends SimpleSelectManyRenderer
{
public SimpleSelectManyCheckboxRenderer()
{
this(CoreSelectManyCheckbox.TYPE);
}
public SimpleSelectManyCheckboxRenderer(FacesBean.Type type)
{
super(type);
}
@Override
protected void findTypeConstants(FacesBean.Type type)
{
super.findTypeConstants(type);
_accessKeyKey = type.findKey("accessKey");
_layoutKey = type.findKey("layout");
}
//
// ENCODE BEHAVIOR
//
@Override
protected void encodeElementContent(
FacesContext context,
RenderingContext arc,
UIComponent component,
FacesBean bean,
List<SelectItem> selectItems,
int[] selectedIndices,
Converter converter,
boolean valuePassThru) throws IOException
{
ResponseWriter writer = context.getResponseWriter();
writer.startElement("span", component);
renderId(context, component);
// Render all generic attributes, except styles (they go on the item),
// and onclick (also on the item, see below)
renderAllAttributes(context, arc, bean, false /*no styles*/);
boolean applyFieldSet = _applyFieldSetWrapper(arc);
if (applyFieldSet)
{
String shortDesc = getShortDesc(bean);
if (shortDesc == null)
{
applyFieldSet = false;
}
else
{
writer.startElement("fieldset", null);
writer.writeAttribute("style", "border:none;margin:0px;padding:0px;", null);
writer.startElement("legend", null);
renderStyleClass(context, arc,
SkinSelectors.HIDDEN_LABEL_STYLE_CLASS);
writer.writeText(shortDesc, "shortDesc");
writer.endElement("legend");
}
}
encodeSelectItems(context, arc, component, bean,
selectItems, selectedIndices, converter,
valuePassThru);
if (applyFieldSet)
{
writer.endElement("fieldset");
}
writer.endElement("span");
}
protected void encodeSelectItems(
FacesContext context,
RenderingContext arc,
UIComponent component,
FacesBean bean,
List<SelectItem> selectItems,
int[] selectedIndices,
Converter converter,
boolean valuePassThru) throws IOException
{
int size = (selectItems == null) ? 0 : selectItems.size();
boolean disabled = getDisabled(bean);
boolean isVertical = !CoreSelectManyCheckbox.LAYOUT_HORIZONTAL.equals(
getLayout(bean));
Object accessKey;
if (supportsAccessKeys(arc))
{
accessKey = getAccessKey(bean);
}
else
{
accessKey = null;
}
String itemOnclick = getItemOnclick(arc, bean);
boolean renderedOne = false;
int selectedCount = selectedIndices.length;
int selectedEntry = 0;
for (int i = 0; i < size; i++)
{
boolean selected = ((selectedEntry < selectedCount) &&
(i == selectedIndices[selectedEntry]));
if (selected)
selectedEntry++;
SelectItem item = selectItems.get(i);
if (encodeSelectItem(context, arc, component, item, converter,
valuePassThru, accessKey,
i, selected, disabled,
renderedOne && isVertical,
itemOnclick))
{
renderedOne = true;
}
}
}
protected boolean encodeSelectItem(
FacesContext context,
RenderingContext arc,
UIComponent component,
SelectItem item,
Converter converter,
boolean valuePassThru,
Object accessKey,
int index,
boolean isSelected,
boolean isDisabled,
boolean renderBreak,
String itemOnclick) throws IOException
{
if (item == null)
return false;
String id = arc.getCurrentClientId();
if (id == null)
return false;
// Create the per-item ID, necessary for generating the <label>
// tag. We use "parentid:_[index]"
StringBuffer subidBuffer = new StringBuffer(id.length() + 4);
subidBuffer.append(id);
subidBuffer.append(":_");
subidBuffer.append(IntegerUtils.getString(index));
String subid = subidBuffer.toString();
Object itemValue = SimpleSelectOneRenderer.getItemValue(context,
component,
item,
converter,
valuePassThru,
index);
FacesBean bean = getFacesBean(component);
ResponseWriter rw = context.getResponseWriter();
// Render a <br> if necessary (in "vertical" alignment)
if (renderBreak)
{
rw.startElement("br", null);
rw.endElement("br");
}
// OK, now render the input control
rw.startElement("input", null);
rw.writeAttribute("type", "checkbox", null);
rw.writeAttribute("name", id, null);
rw.writeAttribute("id", subid, null);
rw.writeAttribute("value", itemValue, null);
rw.writeAttribute("accesskey", accessKey, null);
if (isSelected)
rw.writeAttribute("checked", Boolean.TRUE, null);
if (isDisabled || item.isDisabled())
rw.writeAttribute("disabled", Boolean.TRUE, null);
// =-=AEW Render all the Javascript needed on a per-item basis.
// We could optimize SelectManyCheckbox a bit by gathering
// up the "form event handlers" in one pass (seems to be about
// 8% sower this way)
// Also render the events only if the browser supports JavaScript
if (supportsScripting(arc))
{
rw.writeAttribute("onclick", itemOnclick, null);
renderItemFormEventHandlers(context, bean);
}
rw.endElement("input");
// And render the label
rw.startElement("label", null);
rw.writeAttribute("for", subid, null);
// For reasons that aren't especially clear to me, we're getting
// passed the empty string for our title.
String description = item.getDescription();
if ((description != null) && !"".equals(description))
rw.writeAttribute("title", description, null);
rw.writeText(item.getLabel(), null);
rw.endElement("label");
return true;
}
@Override
protected void renderBetweenNonElements(
FacesContext context,
RenderingContext arc,
UIComponent component,
FacesBean bean) throws IOException
{
if (CoreSelectManyCheckbox.LAYOUT_HORIZONTAL.equals(getLayout(bean)))
context.getResponseWriter().writeText(" ", null);
else
super.renderBetweenNonElements(context, arc, component, bean);
}
@Override
protected void renderId(
FacesContext context,
UIComponent component) throws IOException
{
if (shouldRenderId(context, component))
{
String clientId = getClientId(context, component);
context.getResponseWriter().writeAttribute("id", clientId, "id");
}
}
/**
* Disable rendering the form event handlers on the parent.
* In Gecko, they bubble up, but in IE, they don't, so
* they have to go on the items.
*/
@Override
protected void renderFormEventHandlers(
FacesContext context,
FacesBean bean) throws IOException
{
}
/**
* Disable rendering "onclick" on the parent; it needs to
* go on the individual radio buttons
*/
@Override
protected String getOnclick(
FacesBean bean
)
{
return null;
}
/**
* Render the per-item event handlers
*/
protected void renderItemFormEventHandlers(
FacesContext context,
FacesBean bean) throws IOException
{
super.renderFormEventHandlers(context, bean);
}
/**
* Get the onclick for the individual checkboxes
*/
protected String getItemOnclick(RenderingContext arc, FacesBean bean)
{
// Get the overall onclick, and merge in any needed autosubmit script
String onclick = super.getOnclick(bean);
if (isAutoSubmit(bean))
{
String source = LabelAndMessageRenderer.__getCachedClientId(arc);
boolean immediate = isImmediate(bean);
String auto = AutoSubmitUtils.getSubmitScript(arc,
source,
XhtmlConstants.AUTOSUBMIT_EVENT,
immediate);
onclick = XhtmlUtils.getChainedJS(onclick, auto, true);
}
return onclick;
}
protected Object getAccessKey(FacesBean bean)
{
return bean.getProperty(_accessKeyKey);
}
protected String getLayout(FacesBean bean)
{
return toString(bean.getProperty(_layoutKey));
}
// Never render the "hidden label"; labels entirely go on the individual
// items
@Override
protected boolean isHiddenLabelRequired(RenderingContext arc)
{
return false;
}
@Override
protected String getContentStyleClass(FacesBean bean)
{
return "af|selectManyCheckbox::content";
}
@Override
protected String getRootStyleClass(FacesBean bean)
{
return "af|selectManyCheckbox";
}
private static boolean _applyFieldSetWrapper(RenderingContext arc)
{
// Don't bother with the output in inaccessible mode
if (isInaccessibleMode(arc))
return false;
// The fieldset trick doesn't work without support for hidden labels
if (!HiddenLabelUtils.supportsHiddenLabels(arc))
return true;
return Boolean.TRUE.equals(
arc.getAgent().getCapabilities().get(TrinidadAgent.CAP_FIELDSET));
}
private PropertyKey _accessKeyKey;
private PropertyKey _layoutKey;
}