| /* |
| * Copyright 2009 Google Inc. |
| * |
| * 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. |
| */ |
| package com.google.gwt.user.client.ui; |
| |
| import com.google.gwt.core.client.GWT; |
| import com.google.gwt.dom.client.Element; |
| import com.google.gwt.event.dom.client.ChangeEvent; |
| import com.google.gwt.event.dom.client.ChangeHandler; |
| import com.google.gwt.event.dom.client.HasChangeHandlers; |
| 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.input.shared.ParseException; |
| import com.google.gwt.input.shared.Parser; |
| import com.google.gwt.input.shared.Renderer; |
| import com.google.gwt.user.client.DOM; |
| import com.google.gwt.user.client.Event; |
| import com.google.gwt.user.client.ui.TextBoxBase.TextAlignConstant; |
| import com.google.gwt.user.client.ui.impl.TextBoxImpl; |
| |
| /** |
| * Abstract base class for all text entry widgets. |
| * |
| * @param <T> the value type |
| */ |
| @SuppressWarnings("deprecation") |
| public class ValueBoxBase<T> extends FocusWidget implements |
| SourcesChangeEvents, HasChangeHandlers, HasText, HasName, HasValue<T> { |
| |
| private static TextBoxImpl impl = GWT.create(TextBoxImpl.class); |
| |
| private final Parser<T> parser; |
| private final Renderer<T> renderer; |
| |
| private Event currentEvent; |
| private boolean valueChangeHandlerInitialized; |
| |
| /** |
| * Creates a text box that wraps the given browser element handle. This is |
| * only used by subclasses. |
| * |
| * @param elem the browser element to wrap |
| */ |
| protected ValueBoxBase(Element elem, Renderer<T> renderer, Parser<T> parser) { |
| super(elem); |
| this.renderer = renderer; |
| this.parser = parser; |
| } |
| |
| public HandlerRegistration addChangeHandler(ChangeHandler handler) { |
| return addDomHandler(handler, ChangeEvent.getType()); |
| } |
| |
| /** |
| * @deprecated Use {@link #addChangeHandler} instead |
| */ |
| @Deprecated |
| public void addChangeListener(ChangeListener listener) { |
| addChangeHandler(new ListenerWrapper.WrappedChangeListener(listener)); |
| } |
| |
| public HandlerRegistration addValueChangeHandler(ValueChangeHandler<T> handler) { |
| // Initialization code |
| if (!valueChangeHandlerInitialized) { |
| valueChangeHandlerInitialized = true; |
| addChangeHandler(new ChangeHandler() { |
| public void onChange(ChangeEvent event) { |
| ValueChangeEvent.fire(ValueBoxBase.this, getValue()); |
| } |
| }); |
| } |
| return addHandler(handler, ValueChangeEvent.getType()); |
| } |
| |
| /** |
| * If a keyboard event is currently being handled on this text box, calling |
| * this method will suppress it. This allows listeners to easily filter |
| * keyboard input. |
| */ |
| public void cancelKey() { |
| if (currentEvent != null) { |
| DOM.eventPreventDefault(currentEvent); |
| } |
| } |
| |
| /** |
| * Gets the current position of the cursor (this also serves as the beginning |
| * of the text selection). |
| * |
| * @return the cursor's position |
| */ |
| public int getCursorPos() { |
| return impl.getCursorPos(getElement()); |
| } |
| |
| public String getName() { |
| return DOM.getElementProperty(getElement(), "name"); |
| } |
| |
| /** |
| * Gets the text currently selected within this text box. |
| * |
| * @return the selected text, or an empty string if none is selected |
| */ |
| public String getSelectedText() { |
| int start = getCursorPos(); |
| if (start < 0) { |
| return ""; |
| } |
| int length = getSelectionLength(); |
| return getText().substring(start, start + length); |
| } |
| |
| /** |
| * Gets the length of the current text selection. |
| * |
| * @return the text selection length |
| */ |
| public int getSelectionLength() { |
| return impl.getSelectionLength(getElement()); |
| } |
| |
| public String getText() { |
| return DOM.getElementProperty(getElement(), "value"); |
| } |
| |
| /** |
| * Return the parsed value, or null if the field is empty or parsing fails. |
| */ |
| public T getValue() { |
| try { |
| return getValueOrThrow(); |
| } catch (ParseException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Return the parsed value, or null if the field is empty. |
| * |
| * @throws ParseException if the value cannot be parsed |
| */ |
| public T getValueOrThrow() throws ParseException { |
| String text = getText().trim(); |
| |
| if ("".equals(text)) { |
| return null; |
| } |
| |
| return parser.parse(text); |
| } |
| |
| /** |
| * Determines whether or not the widget is read-only. |
| * |
| * @return <code>true</code> if the widget is currently read-only, |
| * <code>false</code> if the widget is currently editable |
| */ |
| public boolean isReadOnly() { |
| return DOM.getElementPropertyBoolean(getElement(), "readOnly"); |
| } |
| |
| @Override |
| public void onBrowserEvent(Event event) { |
| int type = DOM.eventGetType(event); |
| if ((type & Event.KEYEVENTS) != 0) { |
| // Fire the keyboard event. Hang on to the current event object so that |
| // cancelKey() and setKey() can be implemented. |
| currentEvent = event; |
| // Call the superclass' onBrowserEvent as that includes the key event |
| // handlers. |
| super.onBrowserEvent(event); |
| currentEvent = null; |
| } else { |
| // Handles Focus and Click events. |
| super.onBrowserEvent(event); |
| } |
| } |
| |
| /** |
| * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the |
| * object returned by {@link #addChangeHandler} instead |
| */ |
| @Deprecated |
| public void removeChangeListener(ChangeListener listener) { |
| ListenerWrapper.WrappedChangeListener.remove(this, listener); |
| } |
| |
| /** |
| * Selects all of the text in the box. |
| * |
| * This will only work when the widget is attached to the document and not |
| * hidden. |
| */ |
| public void selectAll() { |
| int length = getText().length(); |
| if (length > 0) { |
| setSelectionRange(0, length); |
| } |
| } |
| |
| /** |
| * Sets the cursor position. |
| * |
| * This will only work when the widget is attached to the document and not |
| * hidden. |
| * |
| * @param pos the new cursor position |
| */ |
| public void setCursorPos(int pos) { |
| setSelectionRange(pos, 0); |
| } |
| |
| /** |
| * If a keyboard event is currently being handled by the text box, this method |
| * replaces the unicode character or key code associated with it. This allows |
| * listeners to easily filter keyboard input. |
| * |
| * @param key the new key value |
| * @deprecated this method only works in IE and should not have been added to |
| * the API |
| */ |
| @Deprecated |
| public void setKey(char key) { |
| if (currentEvent != null) { |
| DOM.eventSetKeyCode(currentEvent, key); |
| } |
| } |
| |
| public void setName(String name) { |
| DOM.setElementProperty(getElement(), "name", name); |
| } |
| |
| /** |
| * Turns read-only mode on or off. |
| * |
| * @param readOnly if <code>true</code>, the widget becomes read-only; if |
| * <code>false</code> the widget becomes editable |
| */ |
| public void setReadOnly(boolean readOnly) { |
| DOM.setElementPropertyBoolean(getElement(), "readOnly", readOnly); |
| String readOnlyStyle = "readonly"; |
| if (readOnly) { |
| addStyleDependentName(readOnlyStyle); |
| } else { |
| removeStyleDependentName(readOnlyStyle); |
| } |
| } |
| |
| /** |
| * Sets the range of text to be selected. |
| * |
| * This will only work when the widget is attached to the document and not |
| * hidden. |
| * |
| * @param pos the position of the first character to be selected |
| * @param length the number of characters to be selected |
| */ |
| public void setSelectionRange(int pos, int length) { |
| // Setting the selection range will not work for unattached elements. |
| if (!isAttached()) { |
| return; |
| } |
| |
| if (length < 0) { |
| throw new IndexOutOfBoundsException( |
| "Length must be a positive integer. Length: " + length); |
| } |
| if ((pos < 0) || (length + pos > getText().length())) { |
| throw new IndexOutOfBoundsException("From Index: " + pos + " To Index: " |
| + (pos + length) + " Text Length: " + getText().length()); |
| } |
| impl.setSelectionRange(getElement(), pos, length); |
| } |
| |
| /** |
| * Sets this object's text. Note that some browsers will manipulate the text |
| * before adding it to the widget. For example, most browsers will strip all |
| * <code>\r</code> from the text, except IE which will add a <code>\r</code> |
| * before each <code>\n</code>. Use {@link #getText()} to get the text |
| * directly from the widget. |
| * |
| * @param text the object's new text |
| */ |
| public void setText(String text) { |
| DOM.setElementProperty(getElement(), "value", text != null ? text : ""); |
| } |
| |
| /** |
| * Sets the alignment of the text in the text box. |
| * |
| * @param align the text alignment (as specified by {@link #ALIGN_CENTER}, |
| * {@link #ALIGN_JUSTIFY}, {@link #ALIGN_LEFT}, and |
| * {@link #ALIGN_RIGHT}) |
| */ |
| public void setTextAlignment(TextAlignConstant align) { |
| DOM.setStyleAttribute(getElement(), "textAlign", align.getTextAlignString()); |
| } |
| |
| public void setValue(T value) { |
| setValue(value, false); |
| } |
| |
| public void setValue(T value, boolean fireEvents) { |
| T oldValue = getValue(); |
| setText(renderer.render(value)); |
| if (fireEvents) { |
| ValueChangeEvent.fireIfNotEqual(this, oldValue, value); |
| } |
| } |
| |
| protected TextBoxImpl getImpl() { |
| return impl; |
| } |
| } |