com.visural.wicket.component.dropdown.DropDown.java Source code

Java tutorial

Introduction

Here is the source code for com.visural.wicket.component.dropdown.DropDown.java

Source

/*
 *  Copyright 2009 Richard Nichols.
 *
 *  Licensed 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.
 *  under the License.
 */
package com.visural.wicket.component.dropdown;

import com.jquery.JQueryBGIFrameResourceReference;
import com.visural.common.StringUtil;
import com.visural.javascript.StringBufferResourceReference;
import com.visural.wicket.security.IPrivilege;
import com.visural.wicket.security.ISecureEnableInstance;
import com.visural.wicket.security.ISecureRenderInstance;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.behavior.AbstractBehavior;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.JavascriptPackageResource;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.IModel;
import org.apache.wicket.util.convert.ConversionException;
import org.apache.wicket.util.convert.IConverter;

/**
 * DropDown is a form component which implements a rich-featured drop down (combo) box. *Requires JQuery*
 *
 * Apply to an `<input type="text"/>` component.
 *
 * The features of this dropdown in comparison to the HTML standard `select` control are:
 *
 *   * Can type into drop down box and the box will automatically appear and filter to input text
 *   * Can choose to filter / show all results
 *   * If multiple drop downs are rendered (say in a list) then they will only render the option values once into the markup, saving bandwidth.
 *   * Allows non-list values to be entered (i.e. choose from list or type your own)
 *   * Allows "must choose from list" mode - i.e. must choose a list value
 *
 * Include in page e.g. - `<input type="text" wicket:id="myDropDown"/>`
 *
 * When the box is working in `"requireListValue = true"` mode, then it the model
 * will expect to store the object of the same type as that stored in the lookup list.
 *
 * When the box is working in `"requireListValue = false"` mode, then it the model
 * will expect to store a String, regardless of the type of object which makes
 * up the lookup list.
 *
 * Limitations:
 *
 *   * onComponentTag() is final as it will have inconsistent results if used in
 *     the usual way. Try overriding `getValueAttributes()` instead.
 *
 * @version $Id: DropDown.java 258 2011-02-24 07:11:45Z tibes80@gmail.com $
 * @author Richard Nichols
 */
public class DropDown<T> extends TextField<T>
        implements Serializable, ISecureEnableInstance, ISecureRenderInstance {
    private static final long serialVersionUID = 1L;

    private final boolean outputPath;

    private final String origMarkupId;
    private final DropDownDataSource source;
    private final boolean requireListValue;
    private boolean enableFiltering = true;
    private boolean enableFilterToggle = true;
    private boolean showArrowIcon = true;
    private Integer overrideWidth = null;

    public DropDown(String id, DropDownDataSource source, boolean requireListValue) {
        this(id, null, source, requireListValue);
    }

    public DropDown(String id, IModel<T> model, DropDownDataSource source, boolean requireListValue) {
        this(id, model, null, source, requireListValue);
    }

    public DropDown(String id, IModel<T> model, Class<T> type, DropDownDataSource source,
            boolean requireListValue) {
        super(id, model, type);
        this.outputPath = Application.get().getDebugSettings().isOutputComponentPath();

        this.source = source;
        this.requireListValue = requireListValue;
        this.setComponentBorder(new DropDownBorder());

        origMarkupId = this.getMarkupId();
        this.setOutputMarkupId(true);
        if (requireListValue) {
            this.setMarkupId("visural_dropdown_id_" + origMarkupId);
        } else {
            this.setMarkupId("visural_dropdown_value_" + origMarkupId);
        }

        add(new AbstractBehavior() {
            @Override
            public void onRendered(Component component) {
                if (DropDown.this.requireListValue) {
                    String value = "";
                    if (DropDown.this.hasRawInput()) {
                        String lookup = DropDown.this.getRawInput();
                        T unsubmittedObject = convertValue(new String[] { lookup });
                        if (unsubmittedObject != null) {
                            value = DropDown.this.source.getDescriptionForValue(unsubmittedObject);
                        }
                    } else if (DropDown.this.getModelObject() != null) {
                        value = DropDown.this.source.getDescriptionForValue(DropDown.this.getModelObject());
                    }
                    component.getResponse()
                            .write(String.format(
                                    "<input %s type='text' %s value=\"%s\" id='visural_dropdown_value_%s' %s/>",
                                    DropDown.this.isEnabled() ? "" : "disabled ", getFlattenedValueAttributes(),
                                    StringUtil.htmlAttributeEscape(value), origMarkupId,
                                    outputPath ? "wicketpath=\"" + getPageRelativePath() + "_vwdd\"" : ""));
                } else {
                    component.getResponse()
                            .write("<input type='hidden' id='visural_dropdown_id_" + origMarkupId + "'/>");
                }
                if (DropDown.this.isShowArrowIcon()) {
                    component.getResponse().write(
                            "<a class=\"visural_dropdown_arrow\" href=\"#\" onclick=\"visural_dropdown_toggle('"
                                    + origMarkupId + "'); return false;\">"
                                    + "<img class='visural_dropdown' alt='[v]' src='"
                                    + urlFor(getDropDownIconReference()) + "'/>" + "</a>");
                }
                component.getResponse()
                        .write("<div id=\"visural_dropdown_" + origMarkupId + "\" class=\"visural_dropdown\" "
                                + "onMouseOver=\"visural_dropdown_mouseover('" + origMarkupId + "')\" "
                                + "onMouseOut=\"visural_dropdown_mouseout('" + origMarkupId + "')\"></div>");
            }
        });
        if (autoAddToHeader()) {
            add(JavascriptPackageResource.getHeaderContribution(DropDown.class, "dropdown.js"));
            add(JavascriptPackageResource.getHeaderContribution(new StringBufferResourceReference()));
            add(getCSSHeaderContribution());
            if (isSupportIE6()) {
                add(JavascriptPackageResource.getHeaderContribution(new JQueryBGIFrameResourceReference()));
            }
        }
        final String dsjs = DropDownDataSourceJSRender.getJS(source);
        add(new HeaderContributor(new IHeaderContributor() {
            public void renderHead(IHeaderResponse arg0) {
                arg0.renderOnDomReadyJavascript(dsjs);
            }
        }));
        add(new HeaderContributor(new IHeaderContributor() {
            public void renderHead(IHeaderResponse arg0) {
                arg0.renderOnDomReadyJavascript(getInitJS());
            }
        }));
    }

    /**
     * Override and return false to suppress static Javascript and CSS contributions.
     * (May be desired if you are concatenating / compressing resources as part of build process)
     * @return
     */
    protected boolean autoAddToHeader() {
        return true;
    }

    @Override
    protected void convertInput() {
        if (!requireListValue) {
            super.convertInput();
        } else {
            setConvertedInput(convertValue(getInputAsArray()));
        }
    }

    @Override
    public IConverter getConverter(Class<?> type) {
        if (requireListValue && source.getValues().size() > 0
                && type.equals(source.getValues().get(0).getClass())) {
            return new IConverter() {
                public Object convertToObject(String listnum, Locale locale) {
                    T converted = null;
                    if (StringUtil.isNotBlankStr(listnum)) {
                        try {
                            converted = (T) source.getValues().get(Integer.parseInt(listnum));
                        } catch (Throwable t) {
                            throw new ConversionException("Could not convert '" + listnum + "' to list index.");
                        }
                    }
                    return converted;
                }

                public String convertToString(Object o, Locale locale) {
                    int idx = source.getValues().indexOf(o);
                    return (idx == -1 ? null : Integer.toString(idx));
                }
            };
        } else {
            return super.getConverter(type);
        }
    }

    @Override
    protected T convertValue(String[] value) throws ConversionException {
        if (!requireListValue) {
            return super.convertValue(value);
        } else {
            String listnum = (value != null && value.length > 0 && value[0] != null ? trim(value[0]) : null);
            T converted = null;
            if (StringUtil.isNotBlankStr(listnum)) {
                try {
                    converted = (T) this.source.getValues().get(Integer.parseInt(listnum));
                } catch (Throwable t) {
                    throw new ConversionException("Could not convert '" + listnum + "' to list index.");
                }
            }
            return converted;
        }
    }

    private String getInitJS() {
        StringBuilder sb = new StringBuilder();
        sb.append("visural_dropdowns['").append(origMarkupId).append("'] = new VisuralDropDown('")
                .append(origMarkupId).append("', '").append(this.source.getName()).append("', ")
                .append(!isRequireListValue()).append(", ").append(isEnableFiltering() ? "true" : "false")
                .append(", ").append(isEnableFilterToggle() ? "true" : "false").append(", function() { ")
                .append(getOnValueChangeHandler()).append(" });\n");
        return sb.toString();
    }

    public final boolean isRequireListValue() {
        return requireListValue;
    }

    public boolean isEnableFiltering() {
        return enableFiltering;
    }

    public DropDown setEnableFiltering(boolean enableFiltering) {
        this.enableFiltering = enableFiltering;
        return this;
    }

    public boolean isEnableFilterToggle() {
        return enableFilterToggle;
    }

    public DropDown setEnableFilterToggle(boolean enableFilterToggle) {
        this.enableFilterToggle = enableFilterToggle;
        return this;
    }

    public boolean isShowArrowIcon() {
        return showArrowIcon;
    }

    public DropDown setShowArrowIcon(boolean showArrowIcon) {
        this.showArrowIcon = showArrowIcon;
        return this;
    }

    @Override
    protected final void onComponentTag(ComponentTag tag) {
        super.onComponentTag(tag);
        // this is a cheat - when requiring a list value we make this object a
        // hidden field, as the ID attribute is the one we're actually interested in.
        if (isRequireListValue()) {
            tag.put("type", "hidden");
        }
        if (!requireListValue) {
            Map<String, String> valueAttributes = getValueAttributes();
            for (String attrib : valueAttributes.keySet()) {
                tag.put(attrib, valueAttributes.get(attrib));
            }
        }
    }

    private String getFlattenedValueAttributes() {
        Map<String, String> valueAttributes = getValueAttributes();
        StringBuffer result = new StringBuffer();
        for (String attrib : valueAttributes.keySet()) {
            result.append(" ");
            result.append(attrib);
            result.append("=\"");
            result.append(StringUtil.htmlAttributeEscape(valueAttributes.get(attrib)));
            result.append("\"");
        }
        return result.toString();
    }

    /**
     * Set the `size` attribute of the drop down entry field.
     * @param width
     */
    public DropDown setCharacterWidth(int width) {
        this.overrideWidth = Integer.valueOf(width);
        return this;
    }

    /**
     * Override to add new attributes to the `<input type="text" .../>` that
     * actually gets generated.
     *
     * Be sure to call super.getValueAttributes() as the basis for your result.
     *
     * @return map of attributes
     */
    protected Map<String, String> getValueAttributes() {
        Map<String, String> result = new HashMap<String, String>();
        result.put("onBlur", "visural_dropdown_blur('" + origMarkupId + "')");
        result.put("onKeyDown", "return visural_dropdown_registerinput(event, '" + origMarkupId + "')");
        result.put("class", "visural_dropdown");
        result.put("onFocus", "visural_dropdown_focus('" + origMarkupId + "')");
        result.put("autocomplete", "off");
        if (overrideWidth != null) {
            result.put("size", overrideWidth.toString());
        }
        return result;
    }

    /**
     * Override to add a javascript call that is invoked whenever the dropdown's actual value changes.
     * @return
     */
    public String getOnValueChangeHandler() {
        return "";
    }

    /**
     * Override this method to enable IE6 support (bgiframe)
     * @return
     */
    public boolean isSupportIE6() {
        return false;
    }

    /**
     * Override to return a different dropdown.css (apply custom dropdown style).
     * @return
     */
    protected HeaderContributor getCSSHeaderContribution() {
        return CSSPackageResource.getHeaderContribution(DropDown.class, "dropdown.css");
    }

    /**
     * Override to change the icon displayed as the "drop down" icon.
     * @return
     */
    protected ResourceReference getDropDownIconReference() {
        return new DropDownImageResourceRef();
    }

    public IPrivilege getRenderPrivilege() {
        return IPrivilege.NULL;
    }

    public IPrivilege getEnablePrivilege() {
        return IPrivilege.NULL;
    }

}