///////////////////////////////////////////////////////////////////////////// // // Project ProjectForge Community Edition // www.projectforge.org // // Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de) // // ProjectForge is dual-licensed. // // This community edition is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 3 of the License. // // This community edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General // Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, see http://www.gnu.org/licenses/. // ///////////////////////////////////////////////////////////////////////////// package org.projectforge.web.wicket.autocompletion; import java.util.List; import org.apache.commons.lang.ObjectUtils; import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteBehavior; import org.apache.wicket.extensions.ajax.markup.html.autocomplete.IAutoCompleteRenderer; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.OnDomReadyHeaderItem; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.util.string.StringValue; import org.projectforge.web.wicket.WicketUtils; public abstract class PFAutoCompleteTextField<T> extends TextField<T> { private static final long serialVersionUID = 3207038195316387588L; /** auto complete behavior attached to this textfield */ private PFAutoCompleteBehavior<T> behavior; private AbstractDefaultAjaxBehavior deleteBehavior; private PFAutoCompleteSettings settings; protected boolean providesTooltip; private boolean tooltipRightAlignment; private IAutoCompleteRenderer<String> renderer; private static final String CONTENT = "delete"; /** * @param id * @param model */ protected PFAutoCompleteTextField(final String id, final IModel<T> model) { this(id, model, false); } /** * @param id * @param model * @param tooltipRightAlignment */ protected PFAutoCompleteTextField(final String id, final IModel<T> model, final boolean tooltipRightAlignment) { this(id, model, PFAutoCompleteRenderer.INSTANCE, new PFAutoCompleteSettings(), tooltipRightAlignment); } protected PFAutoCompleteTextField(final String id, final IModel<T> model, final IAutoCompleteRenderer<String> renderer, final PFAutoCompleteSettings settings) { this(id, model, renderer, settings, false); } protected PFAutoCompleteTextField(final String id, final IModel<T> model, final IAutoCompleteRenderer<String> renderer, final PFAutoCompleteSettings settings, final boolean tooltipRightAlignment) { super(id, model); this.renderer = renderer; this.settings = settings; this.tooltipRightAlignment = tooltipRightAlignment; } /** * @see org.apache.wicket.Component#onInitialize() */ @Override protected void onInitialize() { super.onInitialize(); behavior = new PFAutoCompleteBehavior<T>(renderer, settings) { private static final long serialVersionUID = 1L; @Override protected List<T> getChoices(final String input) { return PFAutoCompleteTextField.this.getChoices(input); } @Override protected List<T> getFavorites() { return PFAutoCompleteTextField.this.getFavorites(); } @Override protected List<String> getRecentUserInputs() { return PFAutoCompleteTextField.this.getRecentUserInputs(); } @Override protected String formatValue(final T value) { return PFAutoCompleteTextField.this.formatValue(value); } @Override protected String formatLabel(final T value) { return PFAutoCompleteTextField.this.formatLabel(value); } }; add(behavior); deleteBehavior = new AbstractDefaultAjaxBehavior() { private static final long serialVersionUID = 3014042180471042845L; @Override protected void respond(final AjaxRequestTarget target) { // Gather query params ?...&content=kssel final StringValue contentValue = RequestCycle.get().getRequest().getQueryParameters().getParameterValue(CONTENT); if (contentValue != null) { final String contentString = contentValue.toString(); if (getForm() instanceof AutoCompleteIgnoreForm) { ((AutoCompleteIgnoreForm) getForm()).ignore(PFAutoCompleteTextField.this, contentString); } // else { just ignore } } } }; add(deleteBehavior); } @Override public void renderHead(final IHeaderResponse response) { super.renderHead(response); response.render(OnDomReadyHeaderItem.forScript("$('#" + this.getMarkupId() + "').data('callback', '" + deleteBehavior.getCallbackUrl() + "');")); } @SuppressWarnings("serial") public PFAutoCompleteTextField<T> enableTooltips() { WicketUtils.addTooltip(this, new Model<String>() { @Override public String getObject() { return PFAutoCompleteTextField.this.getTooltip(); } }, tooltipRightAlignment); return this; } /** {@inheritDoc} */ @Override protected void onComponentTag(final ComponentTag tag) { super.onComponentTag(tag); // disable browser's autocomplete tag.put("autocomplete", "off"); } /** * Override this callback method that for returning favorite entries to show, if the user double clicks the empty input field. These * objects will be passed to the renderer to generate output. * * @see AutoCompleteBehavior#getChoices(String) * * @return null, if no favorites to show. */ protected List<T> getFavorites() { return null; } /** * Override this callback method that for returning recent user inputs to show, if the user double clicks the empty input field. * * @return null means: don't show recent user inputs. */ protected List<String> getRecentUserInputs() { return null; } /** * Uses ObjectUtils.toString(Object) as default. * @param value * @return */ protected String formatValue(final T value) { return ObjectUtils.toString(value); } /** * Only used if labelValue is set to true. * @param value * @return The label to show in the drop down choice. If not overloaded null is returned. */ protected String formatLabel(final T value) { return null; } /** * Overwrite this method if a title attribute for the input text field should be set. Don't forget to call {@link #enableTooltips()}. * @return Tool-tip of the object currently represented by the input field or null. */ protected String getTooltip() { return null; } /** * Callback method that should return a list of all possible assist choice objects. These objects will be passed to the renderer to * generate output. * * @see AutoCompleteBehavior#getChoices(String) * * @param input current input * @return list of all possible choice objects */ protected abstract List<T> getChoices(String input); /** * Fluent. * @see PFAutoCompleteSettings#withAutoFill(boolean) */ public PFAutoCompleteTextField<T> withAutoFill(final boolean autoFill) { settings.withAutoFill(autoFill); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withAutoSubmit(boolean) */ public PFAutoCompleteTextField<T> withAutoSubmit(final boolean autoSubmit) { settings.withAutoSubmit(autoSubmit); return this; } /** * @return the settings */ public PFAutoCompleteSettings getSettings() { return settings; } /** * Fluent. * @see PFAutoCompleteSettings#withFocus(boolean) */ public PFAutoCompleteTextField<T> withFocus(final boolean hasFocus) { settings.withFocus(hasFocus); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withCacheLength(int) */ public PFAutoCompleteTextField<T> withCacheLength(final int cacheLength) { settings.withCacheLength(cacheLength); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withDelay(int) */ public PFAutoCompleteTextField<T> withDelay(final int delay) { settings.withDelay(delay); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withMatchCase(boolean) */ public PFAutoCompleteTextField<T> withMatchCase(final boolean matchCase) { settings.withMatchCase(matchCase); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withMatchContains(boolean) */ public PFAutoCompleteTextField<T> withMatchContains(final boolean matchContains) { settings.withMatchContains(matchContains); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withMatchSubset(boolean) */ public PFAutoCompleteTextField<T> withMatchSubset(final boolean matchSubset) { settings.withMatchSubset(matchSubset); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withMaxItemsToShow(int) */ public PFAutoCompleteTextField<T> withMaxItemsToShow(final int maxItemsToShow) { settings.withMaxItemsToShow(maxItemsToShow); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withMinChars(int) */ public PFAutoCompleteTextField<T> withMinChars(final int minChars) { settings.withMinChars(minChars); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withMustMatch(boolean) */ public PFAutoCompleteTextField<T> withMustMatch(final boolean mustMatch) { settings.withMustMatch(mustMatch); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withScroll(boolean) */ public PFAutoCompleteTextField<T> withScroll(final boolean scroll) { settings.withScroll(scroll); return this; } /** * Fluent. */ public PFAutoCompleteTextField<T> withScrollHeight(final int scrollHeight) { settings.withScrollHeight(scrollHeight); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withSelectFirst(boolean) */ public PFAutoCompleteTextField<T> withSelectFirst(final boolean selectFirst) { settings.withSelectFirst(selectFirst); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withSelectOnly(boolean) */ public PFAutoCompleteTextField<T> withSelectOnly(final boolean selectOnly) { settings.withSelectOnly(selectOnly); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withWidth(int) */ public PFAutoCompleteTextField<T> withWidth(final int width) { settings.withWidth(width); return this; } /** * Fluent. * @see PFAutoCompleteSettings#withLabelValue(boolean) */ public PFAutoCompleteTextField<T> withLabelValue(final boolean labelValue) { settings.withLabelValue(labelValue); return this; } /** * Fluent. */ public PFAutoCompleteTextField<T> withDeletableItem(final boolean deletableItem) { settings.setDeletableItem(deletableItem); return this; } }