protected void encodeMarkup(FacesContext context, SelectCheckboxMenu menu) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String clientId = menu.getClientId(context);
    List<SelectItem> selectItems = getSelectItems(context, menu);
    boolean valid = menu.isValid();

    String style = menu.getStyle();
    String styleclass = menu.getStyleClass();
    styleclass =
        styleclass == null
            ? SelectCheckboxMenu.STYLE_CLASS
            : SelectCheckboxMenu.STYLE_CLASS + " " + styleclass;
    styleclass = menu.isDisabled() ? styleclass + " ui-state-disabled" : styleclass;
    styleclass = !valid ? styleclass + " ui-state-error" : styleclass;

    writer.startElement("div", menu);
    writer.writeAttribute("id", clientId, "id");
    writer.writeAttribute("class", styleclass, "styleclass");
    if (style != null) {
      writer.writeAttribute("style", style, "style");
    }

    encodeInputs(context, menu, selectItems);
    encodeLabel(context, menu, selectItems, valid);
    encodeMenuIcon(context, menu, valid);

    writer.endElement("div");
  }
  protected void encodeLabel(
      FacesContext context, SelectCheckboxMenu menu, List<SelectItem> selectItems, boolean valid)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String label = menu.getLabel();
    String labelClass =
        !valid
            ? SelectCheckboxMenu.LABEL_CLASS + " ui-state-error"
            : SelectCheckboxMenu.LABEL_CLASS;
    if (label == null) {
      label = "&nbsp;";
    }

    writer.startElement("a", null);
    writer.writeAttribute("href", "#", null);
    writer.writeAttribute("class", SelectCheckboxMenu.LABEL_CONTAINER_CLASS, null);
    if (menu.getTabindex() != null) {
      writer.writeAttribute("tabindex", menu.getTabindex(), null);
    }

    writer.startElement("label", null);
    writer.writeAttribute("class", labelClass, null);
    writer.write(label);
    writer.endElement("label");
    writer.endElement("a");
  }
  protected void encodeOption(
      FacesContext context,
      SelectCheckboxMenu menu,
      Object values,
      Object submittedValues,
      Converter converter,
      SelectItem option,
      int idx)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String itemValueAsString = getOptionAsString(context, menu, converter, option.getValue());
    String name = menu.getClientId(context);
    String id = name + UINamingContainer.getSeparatorChar(context) + idx;
    boolean disabled = option.isDisabled() || menu.isDisabled();

    Object valuesArray;
    Object itemValue;
    if (submittedValues != null) {
      valuesArray = submittedValues;
      itemValue = itemValueAsString;
    } else {
      valuesArray = values;
      itemValue = option.getValue();
    }

    boolean checked = isSelected(context, menu, itemValue, valuesArray, converter);
    if (option.isNoSelectionOption() && values != null && !checked) {
      return;
    }

    // input
    writer.startElement("input", null);
    writer.writeAttribute("id", id, null);
    writer.writeAttribute("name", name, null);
    writer.writeAttribute("type", "checkbox", null);
    writer.writeAttribute("value", itemValueAsString, null);

    if (checked) writer.writeAttribute("checked", "checked", null);
    if (disabled) writer.writeAttribute("disabled", "disabled", null);
    if (menu.getOnchange() != null) writer.writeAttribute("onchange", menu.getOnchange(), null);

    writer.endElement("input");

    // label
    writer.startElement("label", null);
    writer.writeAttribute("for", id, null);
    if (disabled) writer.writeAttribute("class", "ui-state-disabled", null);

    if (option.isEscape()) writer.writeText(option.getLabel(), null);
    else writer.write(option.getLabel());

    writer.endElement("label");
  }
  protected void encodeInputs(
      FacesContext context, SelectCheckboxMenu menu, List<SelectItem> selectItems)
      throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    Converter converter = menu.getConverter();
    Object values = getValues(menu);
    Object submittedValues = getSubmittedValues(menu);

    writer.startElement("div", menu);
    writer.writeAttribute("class", "ui-helper-hidden", null);

    int idx = -1;
    for (SelectItem selectItem : selectItems) {
      idx++;

      encodeOption(context, menu, values, submittedValues, converter, selectItem, idx);
    }

    writer.endElement("div");
  }
  protected void encodeScript(FacesContext context, SelectCheckboxMenu menu) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String clientId = menu.getClientId(context);

    WidgetBuilder wb = getWidgetBuilder(context);
    wb.widget("SelectCheckboxMenu", menu.resolveWidgetVar(), clientId, true)
        .callback("onShow", "function()", menu.getOnShow())
        .callback("onHide", "function()", menu.getOnHide())
        .attr("scrollHeight", menu.getScrollHeight(), Integer.MAX_VALUE);

    if (menu.isFilter()) {
      wb.attr("filter", true)
          .attr("filterMatchMode", menu.getFilterMatchMode(), null)
          .attr("filterFunction", menu.getFilterFunction(), null)
          .attr("caseSensitive", menu.isCaseSensitive(), false);
    }

    wb.attr("panelStyle", menu.getPanelStyle(), null)
        .attr("panelStyleClass", menu.getPanelStyleClass(), null);

    encodeClientBehaviors(context, menu, wb);

    startScript(writer, clientId);
    writer.write(wb.build());
    endScript(writer);
  }