Java tutorial
/* * 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; } }