Java tutorial
/* * Tatami: * Copyright (C) 2007 Objet Direct * Copyright (C) 2007 France Telecom * Contact: tatami@googlegroups.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * Author: Ronan Dunklau * Initial developer(s): * Contributor(s): */ package com.objetdirect.tatami.client; import java.util.HashMap; import java.util.Map; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.ChangeListener; import com.google.gwt.user.client.ui.ChangeListenerCollection; import com.google.gwt.user.client.ui.HasValue; /** * This class wraps the dojo NumberSpinner. * It's a simple number spinner with typematic and validation constraints * * @author rdunklau * */ public class NumberSpinner extends AbstractDojoFocus implements HasValue<Number> { /** * Constant used to specify the spinner minimum value * @see #addConstraint(String, String) * @see #NumberSpinner(int, String, boolean, float, String, String, float, boolean, float, Map) * @see #NumberSpinner(String, float, String, String, float, Map) */ public static final String CONSTRAINT_MIN = "min"; /** * Constant used to specify the spinner maximum value * @see #addConstraint(String, String) * @see #NumberSpinner(int, String, boolean, float, String, String, float, boolean, float, Map) * @see #NumberSpinner(String, float, String, String, float, Map) */ public static final String CONSTRAINT_MAX = "max"; /** * Constant used to specify the spinner number pattern. * @see #addConstraint(String, String) * @see #NumberSpinner(int, String, boolean, float, String, String, float, boolean, float, Map) * @see #NumberSpinner(String, float, String, String, float, Map) */ public static final String CONSTRAINT_PATTERN = "pattern"; /** * Constant used to specify the spinner number type (percent, currency, decimal). * @see #addConstraint(String, String) * @see #NumberSpinner(int, String, boolean, float, String, String, float, boolean, float, Map) * @see #NumberSpinner(String, float, String, String, float, Map) */ public static final String CONSTRAINT_TYPE = "type"; /** * Constant used to specify the spinner currency symbol * @see #addConstraint(String, String) * @see #NumberSpinner(int, String, boolean, float, String, String, float, boolean, float, Map) * @see #NumberSpinner(String, float, String, String, float, Map) */ public static final String CONSTRAINT_CURRENCY = "currency"; /** * @deprecated */ protected ChangeListenerCollection changeListeners; /** * Delay until the "TypeMatic" is triggered (in ms) * TypeMatic is what permits the spinner to spin more quickly * when the user performs a "long click" * */ private int defaultTimeout = 500; /** * Defines the speed at which the typematic increases the spinner */ private float timeoutChangeRate = 0.90f; /** * Message to display when the constraints are not respected (@see constraints) * */ private String invalidMessage = "Invalid Value"; /** * Indicates wether to fire "onChange" events each time the * spinner value change * * if true : a simple press over the spinner arrows fire an onChange event * * if false : onChange events are fired only when the spinner loses focus * */ private boolean intermediateChanges = true; /** * Increment which is used when the user click on the spinner arrows (or up/down keys) */ private float smallDelta = 1f; /** * Increment which is used when the user uses pageUp / pageDown * (Not yet implemented by underlying JavaScript Widget !) * */ private float largeDelta = 10f; /** * Hint String to be displayed to help the user * * */ private String promptMessage = "Hint String"; /** * String to be displayed when the value is out of range * (ie, it is not comprised between the constraints "min" & "max" values * */ private String rangeMessage = "Out of range Value"; /** * * Initial value * */ private float value = 0; /** * Wether to trim the spinner's content * */ private boolean trim = false; /** * Constraints on the spinner content. * * Principal constraints keys : * * Min : minimum value * Max : maximum value * pattern : Unicode standard number pattern used to format numbers * (for exemple, number : 1234.567 with pattern #,##0.### * will be displayed 1 234,567, according to the locale) * places : number of decimal places to accept. This value is STRICT (ie, * if places = 2 , the user cannot enter value 6.0) * * * * @see http://dojotoolkit.org/book/dojo-book-0-9/part-2-dijit/form-validation-specialized-input/textbox-validating-currency-number * * */ private Map<String, Object> constraints = new HashMap<String, Object>(); /** * * Constructor using all attributes. * See attributes comments for detailed explanation * * * * @param defaultTimeout * @param invalidMessage * @param intermediateChanges * @param smallDelta * @param largeDelta * @param promptMessage * @param rangeMessage * @param timeoutChangeRate * @param trim * @param value * @param constraints */ public NumberSpinner(int defaultTimeout, String invalidMessage, boolean intermediateChanges, float delta, String promptMessage, String rangeMessage, float timeoutChangeRate, boolean trim, float value, Map<String, Object> constraints) { this(); this.defaultTimeout = defaultTimeout; this.invalidMessage = invalidMessage; this.intermediateChanges = intermediateChanges; this.smallDelta = delta; this.promptMessage = promptMessage; this.rangeMessage = rangeMessage; this.timeoutChangeRate = timeoutChangeRate; this.trim = trim; this.value = value; this.constraints = constraints; } /** * * @param defaultTimeout * @param invalidMessage * @param intermediateChanges * @param smallDelta * @param largeDelta * @param promptMessage * @param rangeMessage * @param timeoutChangeRate * @param trim * @param value * @param minValue * @param maxValue */ public NumberSpinner(int defaultTimeout, String invalidMessage, boolean intermediateChanges, float smallDelta, float largeDelta, String promptMessage, String rangeMessage, float timeoutChangeRate, boolean trim, float value, float minValue, float maxValue) { this(); this.defaultTimeout = defaultTimeout; this.invalidMessage = invalidMessage; this.intermediateChanges = intermediateChanges; this.smallDelta = smallDelta; this.largeDelta = largeDelta; this.promptMessage = promptMessage; this.rangeMessage = rangeMessage; this.timeoutChangeRate = timeoutChangeRate; this.trim = trim; this.value = value; constraints.put("min", new Float(minValue)); constraints.put("max", new Float(maxValue)); } /** * * @param invalidMessage * @param smallDelta * @param promptMessage * @param rangeMessage * @param value * @param constraints */ public NumberSpinner(String invalidMessage, float smallDelta, String promptMessage, String rangeMessage, float value, Map<String, Object> constraints) { this(); this.promptMessage = promptMessage; this.rangeMessage = rangeMessage; this.invalidMessage = invalidMessage; this.smallDelta = smallDelta; this.value = value; this.constraints = constraints; } public NumberSpinner(float initValue, float minValue, float maxValue, float delta) { this(); constraints.put("min", minValue); constraints.put("max", maxValue); this.smallDelta = delta; this.value = initValue; } /** * Default constructor. * It takes all the default values to create the spinner. * */ public NumberSpinner(Element element) { super(Document.get().createDivElement()); changeListeners = new ChangeListenerCollection(); constraints = new HashMap<String, Object>(); } public NumberSpinner() { this(Document.get().createDivElement()); } private native void dojoSetConstraints(JavaScriptObject constraints, JavaScriptObject dojoWidget)/*-{ dojoWidget.constraints = constraints; }-*/; /** * @return : the spinner's increment */ public float getDelta() { return smallDelta; } public void setDelta(float smallDelta) { this.smallDelta = smallDelta; if (dojoWidget != null) { dojoSetDelta(smallDelta, dojoWidget); } } private native void dojoSetDelta(float delta, JavaScriptObject dojoWidget)/*-{ dojoWidget.smallDelta = delta; }-*/; /** * @return the constraints' HashMap */ public Map<String, Object> getConstraints() { return constraints; } /** * @param constraints : the constraints to be applied to the spinner * {@link #constraints} */ public void setConstraints(Map<String, Object> constraints) { this.constraints = constraints; if (dojoWidget != null) { dojoSetConstraints(JSHelper.convertObjectToJSObject(constraints), dojoWidget); } } /*** * Adds a constraint to the spinner * * @param constraintName : the constraint name. It can be one of: * @link NumberSpinner#CONSTRAINT_MIN * @link NumberSpinner#CONSTRAINT_MAX * @link NumberSpinner#CONSTRAINT_PATTERN * @link NumberSpinner#CONSTRAINT_TYPE * @link NumberSpinner#CONSTRAINT_CURRENCY * * @param value */ public void addConstraint(String constraintName, String value) { constraints.put(constraintName, value); if (dojoWidget != null) { dojoSetConstraints(JSHelper.convertObjectToJSObject(constraints), dojoWidget); } } /*** * Removes a constraint from the spinner * * @param constraintName : the constraint name. It can be one of: * @link NumberSpinner#CONSTRAINT_MIN * @link NumberSpinner#CONSTRAINT_MAX * @link NumberSpinner#CONSTRAINT_PATTERN * @link NumberSpinner#CONSTRAINT_TYPE * @link NumberSpinner#CONSTRAINT_CURRENCY * * @param value */ public void removeConstraint(String constraintName) { constraints.remove(constraintName); if (dojoWidget != null) { dojoSetConstraints(JSHelper.convertObjectToJSObject(constraints), dojoWidget); } } /** * A JSNI method which declare our TatamiNumberSpinner. * TatamiNumberSpinner is the same as Dojo NumberSpinner, except * that we override the onChange function to call the GWT widget's "notifyChangeListeners" * method. */ private native void defineTatamiNumberSpinner()/*-{ $wnd.dojo.declare("dojox.form.TatamiNumberSpinner", $wnd.dijit.form.NumberSpinner , { onChange:function (e) { this.gwtWidget.@com.objetdirect.tatami.client.NumberSpinner::notifyChange()(); }}); }-*/; /** * Instantiates the dojo widget * */ public void onDojoLoad() { defineTatamiNumberSpinner(); } /** * Instantiates the dojo widget * * * @param defaultTimeout * @param invalidMessage * @param intermediateChanges * @param smallDelta * @param largeDelta * @param promptMessage * @param rangeMessage * @param timeoutChangeRate * @param trim * @param value * @param constraints * @return */ private native JavaScriptObject createDojoSpinner(int defaultTimeout, String invalidMessage, boolean intermediateChanges, float smallDelta, float largeDelta, String promptMessage, String rangeMessage, float timeoutChangeRate, boolean trim, float value, JavaScriptObject constraints) /*-{ var spinner = new $wnd.dojox.form.TatamiNumberSpinner( { defaultTimeout : defaultTimeout, invalidMessage : invalidMessage, intermediateChanges : intermediateChanges, smallDelta : smallDelta, largeDelta : largeDelta, promptMessage : promptMessage, rangeMessage : rangeMessage, timeoutChangeRate : timeoutChangeRate, trim : trim, value : value, constraints : constraints } ); return spinner; }-*/ ; /** * This method is called when the dojo widget raises an "onChange" event. * It notifies the ChangeListeners */ private void notifyChange() { this.value = dojoGetValue(dojoWidget); ValueChangeEvent.fire(this, value); if (this.changeListeners != null) { changeListeners.fireChange(this); } } @Override /** * @deprecated */ public void addChangeListener(ChangeListener listener) { changeListeners.add(listener); } /** * @deprecated */ @Override public void removeChangeListener(ChangeListener listener) { changeListeners.remove(listener); } private native float dojoGetValue(JavaScriptObject dojoWidget) /*-{ return dojoWidget.getValue(); }-*/ ; private native void dojoSetValue(JavaScriptObject dojoWidget, float value) /*-{ dojoWidget.setValue(value); }-*/ ; public String getDojoName() { return "dijit.form.NumberSpinner"; } public void createDojoWidget() throws Exception { this.dojoWidget = createDojoSpinner(defaultTimeout, invalidMessage, intermediateChanges, smallDelta, largeDelta, promptMessage, rangeMessage, timeoutChangeRate, trim, value, JSHelper.convertObjectToJSObject(constraints)); } public void doAfterCreation() { DojoController.startup(this); // setBrowserEventCallback(getDojoWidget()); } public void doBeforeDestruction() { } public void free() { destroyWidget(dojoWidget); this.dojoWidget = null; } private native void destroyWidget(JavaScriptObject dojoWidget)/*-{ dojoWidget.destroy(); }-*/; /** * This method arms some callback * @param dojoWidget the DOJO widget. */ private native void setBrowserEventCallback(JavaScriptObject dojoWidget) /*-{ dojoWidget.textbox.onbrowserevent = function(e) { dojoWidget.gwtWidget.@com.objetdirect.tatami.client.DropdownContainer::onBrowserEvent(Lcom/google/gwt/user/client/Event;)(e); }; }-*/; /** * @return true if the spinner fires an on change event each time an arrow is pressed, * false if it fires it only when it loses focus */ public boolean isIntermediateChanges() { return intermediateChanges; } /** * @param intermediateChanges : true if the spinner fires an on change event each time an arrow is pressed, * false if it fires it only when it loses focus */ public void setIntermediateChanges(boolean intermediateChanges) { this.intermediateChanges = intermediateChanges; if (dojoWidget != null) { setIntermediateChanges(intermediateChanges, dojoWidget); } } private native void setIntermediateChanges(boolean intermediateChanges, JavaScriptObject dojoWidget)/*-{ dojoWidget.intermediateChanges = intermediateChanges; }-*/; public Number getValue() { if (this.dojoWidget != null) { this.value = dojoGetValue(dojoWidget); } return this.value; } public void setValue(Number number) { this.value = number.floatValue(); if (dojoWidget != null) { dojoSetValue(dojoWidget, this.value); } } public void setValue(Number number, boolean fire) { setValue(number); notifyChange(); } public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Number> handler) { return addHandler(handler, ValueChangeEvent.getType()); } }