0N/A/*
3261N/A * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/Apackage javax.swing.text;
0N/A
5429N/Aimport sun.reflect.misc.ConstructorUtil;
5429N/A
0N/Aimport java.io.Serializable;
0N/Aimport java.lang.reflect.*;
0N/Aimport java.text.ParseException;
0N/Aimport javax.swing.*;
0N/Aimport javax.swing.text.*;
0N/A
0N/A/**
0N/A * <code>DefaultFormatter</code> formats aribtrary objects. Formatting is done
0N/A * by invoking the <code>toString</code> method. In order to convert the
0N/A * value back to a String, your class must provide a constructor that
0N/A * takes a String argument. If no single argument constructor that takes a
0N/A * String is found, the returned value will be the String passed into
0N/A * <code>stringToValue</code>.
0N/A * <p>
0N/A * Instances of <code>DefaultFormatter</code> can not be used in multiple
0N/A * instances of <code>JFormattedTextField</code>. To obtain a copy of
0N/A * an already configured <code>DefaultFormatter</code>, use the
0N/A * <code>clone</code> method.
0N/A * <p>
0N/A * <strong>Warning:</strong>
0N/A * Serialized objects of this class will not be compatible with
0N/A * future Swing releases. The current serialization support is
0N/A * appropriate for short term storage or RMI between applications running
0N/A * the same version of Swing. As of 1.4, support for long term storage
0N/A * of all JavaBeans<sup><font size="-2">TM</font></sup>
0N/A * has been added to the <code>java.beans</code> package.
0N/A * Please see {@link java.beans.XMLEncoder}.
0N/A *
0N/A * @see javax.swing.JFormattedTextField.AbstractFormatter
0N/A *
0N/A * @since 1.4
0N/A */
0N/Apublic class DefaultFormatter extends JFormattedTextField.AbstractFormatter
0N/A implements Cloneable, Serializable {
0N/A /** Indicates if the value being edited must match the mask. */
0N/A private boolean allowsInvalid;
0N/A
0N/A /** If true, editing mode is in overwrite (or strikethough). */
0N/A private boolean overwriteMode;
0N/A
0N/A /** If true, any time a valid edit happens commitEdit is invoked. */
0N/A private boolean commitOnEdit;
0N/A
0N/A /** Class used to create new instances. */
611N/A private Class<?> valueClass;
0N/A
0N/A /** NavigationFilter that forwards calls back to DefaultFormatter. */
0N/A private NavigationFilter navigationFilter;
0N/A
0N/A /** DocumentFilter that forwards calls back to DefaultFormatter. */
0N/A private DocumentFilter documentFilter;
0N/A
0N/A /** Used during replace to track the region to replace. */
0N/A transient ReplaceHolder replaceHolder;
0N/A
0N/A
0N/A /**
0N/A * Creates a DefaultFormatter.
0N/A */
0N/A public DefaultFormatter() {
0N/A overwriteMode = true;
0N/A allowsInvalid = true;
0N/A }
0N/A
0N/A /**
0N/A * Installs the <code>DefaultFormatter</code> onto a particular
0N/A * <code>JFormattedTextField</code>.
0N/A * This will invoke <code>valueToString</code> to convert the
0N/A * current value from the <code>JFormattedTextField</code> to
0N/A * a String. This will then install the <code>Action</code>s from
0N/A * <code>getActions</code>, the <code>DocumentFilter</code>
0N/A * returned from <code>getDocumentFilter</code> and the
0N/A * <code>NavigationFilter</code> returned from
0N/A * <code>getNavigationFilter</code> onto the
0N/A * <code>JFormattedTextField</code>.
0N/A * <p>
0N/A * Subclasses will typically only need to override this if they
0N/A * wish to install additional listeners on the
0N/A * <code>JFormattedTextField</code>.
0N/A * <p>
0N/A * If there is a <code>ParseException</code> in converting the
0N/A * current value to a String, this will set the text to an empty
0N/A * String, and mark the <code>JFormattedTextField</code> as being
0N/A * in an invalid state.
0N/A * <p>
0N/A * While this is a public method, this is typically only useful
0N/A * for subclassers of <code>JFormattedTextField</code>.
0N/A * <code>JFormattedTextField</code> will invoke this method at
0N/A * the appropriate times when the value changes, or its internal
0N/A * state changes.
0N/A *
0N/A * @param ftf JFormattedTextField to format for, may be null indicating
0N/A * uninstall from current JFormattedTextField.
0N/A */
0N/A public void install(JFormattedTextField ftf) {
0N/A super.install(ftf);
0N/A positionCursorAtInitialLocation();
0N/A }
0N/A
0N/A /**
0N/A * Sets when edits are published back to the
0N/A * <code>JFormattedTextField</code>. If true, <code>commitEdit</code>
0N/A * is invoked after every valid edit (any time the text is edited). On
0N/A * the other hand, if this is false than the <code>DefaultFormatter</code>
0N/A * does not publish edits back to the <code>JFormattedTextField</code>.
0N/A * As such, the only time the value of the <code>JFormattedTextField</code>
0N/A * will change is when <code>commitEdit</code> is invoked on
0N/A * <code>JFormattedTextField</code>, typically when enter is pressed
0N/A * or focus leaves the <code>JFormattedTextField</code>.
0N/A *
0N/A * @param commit Used to indicate when edits are commited back to the
0N/A * JTextComponent
0N/A */
0N/A public void setCommitsOnValidEdit(boolean commit) {
0N/A commitOnEdit = commit;
0N/A }
0N/A
0N/A /**
0N/A * Returns when edits are published back to the
0N/A * <code>JFormattedTextField</code>.
0N/A *
0N/A * @return true if edits are commited after evey valid edit
0N/A */
0N/A public boolean getCommitsOnValidEdit() {
0N/A return commitOnEdit;
0N/A }
0N/A
0N/A /**
0N/A * Configures the behavior when inserting characters. If
0N/A * <code>overwriteMode</code> is true (the default), new characters
0N/A * overwrite existing characters in the model.
0N/A *
0N/A * @param overwriteMode Indicates if overwrite or overstrike mode is used
0N/A */
0N/A public void setOverwriteMode(boolean overwriteMode) {
0N/A this.overwriteMode = overwriteMode;
0N/A }
0N/A
0N/A /**
0N/A * Returns the behavior when inserting characters.
0N/A *
0N/A * @return true if newly inserted characters overwrite existing characters
0N/A */
0N/A public boolean getOverwriteMode() {
0N/A return overwriteMode;
0N/A }
0N/A
0N/A /**
0N/A * Sets whether or not the value being edited is allowed to be invalid
0N/A * for a length of time (that is, <code>stringToValue</code> throws
0N/A * a <code>ParseException</code>).
0N/A * It is often convenient to allow the user to temporarily input an
0N/A * invalid value.
0N/A *
0N/A * @param allowsInvalid Used to indicate if the edited value must always
0N/A * be valid
0N/A */
0N/A public void setAllowsInvalid(boolean allowsInvalid) {
0N/A this.allowsInvalid = allowsInvalid;
0N/A }
0N/A
0N/A /**
0N/A * Returns whether or not the value being edited is allowed to be invalid
0N/A * for a length of time.
0N/A *
0N/A * @return false if the edited value must always be valid
0N/A */
0N/A public boolean getAllowsInvalid() {
0N/A return allowsInvalid;
0N/A }
0N/A
0N/A /**
0N/A * Sets that class that is used to create new Objects. If the
0N/A * passed in class does not have a single argument constructor that
0N/A * takes a String, String values will be used.
0N/A *
0N/A * @param valueClass Class used to construct return value from
0N/A * stringToValue
0N/A */
0N/A public void setValueClass(Class<?> valueClass) {
0N/A this.valueClass = valueClass;
0N/A }
0N/A
0N/A /**
0N/A * Returns that class that is used to create new Objects.
0N/A *
0N/A * @return Class used to constuct return value from stringToValue
0N/A */
0N/A public Class<?> getValueClass() {
0N/A return valueClass;
0N/A }
0N/A
0N/A /**
0N/A * Converts the passed in String into an instance of
0N/A * <code>getValueClass</code> by way of the constructor that
0N/A * takes a String argument. If <code>getValueClass</code>
0N/A * returns null, the Class of the current value in the
0N/A * <code>JFormattedTextField</code> will be used. If this is null, a
0N/A * String will be returned. If the constructor thows an exception, a
0N/A * <code>ParseException</code> will be thrown. If there is no single
0N/A * argument String constructor, <code>string</code> will be returned.
0N/A *
0N/A * @throws ParseException if there is an error in the conversion
0N/A * @param string String to convert
0N/A * @return Object representation of text
0N/A */
0N/A public Object stringToValue(String string) throws ParseException {
611N/A Class<?> vc = getValueClass();
0N/A JFormattedTextField ftf = getFormattedTextField();
0N/A
0N/A if (vc == null && ftf != null) {
0N/A Object value = ftf.getValue();
0N/A
0N/A if (value != null) {
0N/A vc = value.getClass();
0N/A }
0N/A }
0N/A if (vc != null) {
0N/A Constructor cons;
0N/A
0N/A try {
5429N/A cons = ConstructorUtil.getConstructor(vc, new Class[]{String.class});
0N/A
0N/A } catch (NoSuchMethodException nsme) {
0N/A cons = null;
0N/A }
0N/A
0N/A if (cons != null) {
0N/A try {
0N/A return cons.newInstance(new Object[] { string });
0N/A } catch (Throwable ex) {
0N/A throw new ParseException("Error creating instance", 0);
0N/A }
0N/A }
0N/A }
0N/A return string;
0N/A }
0N/A
0N/A /**
0N/A * Converts the passed in Object into a String by way of the
0N/A * <code>toString</code> method.
0N/A *
0N/A * @throws ParseException if there is an error in the conversion
0N/A * @param value Value to convert
0N/A * @return String representation of value
0N/A */
0N/A public String valueToString(Object value) throws ParseException {
0N/A if (value == null) {
0N/A return "";
0N/A }
0N/A return value.toString();
0N/A }
0N/A
0N/A /**
0N/A * Returns the <code>DocumentFilter</code> used to restrict the characters
0N/A * that can be input into the <code>JFormattedTextField</code>.
0N/A *
0N/A * @return DocumentFilter to restrict edits
0N/A */
0N/A protected DocumentFilter getDocumentFilter() {
0N/A if (documentFilter == null) {
0N/A documentFilter = new DefaultDocumentFilter();
0N/A }
0N/A return documentFilter;
0N/A }
0N/A
0N/A /**
0N/A * Returns the <code>NavigationFilter</code> used to restrict where the
0N/A * cursor can be placed.
0N/A *
0N/A * @return NavigationFilter to restrict navigation
0N/A */
0N/A protected NavigationFilter getNavigationFilter() {
0N/A if (navigationFilter == null) {
0N/A navigationFilter = new DefaultNavigationFilter();
0N/A }
0N/A return navigationFilter;
0N/A }
0N/A
0N/A /**
0N/A * Creates a copy of the DefaultFormatter.
0N/A *
0N/A * @return copy of the DefaultFormatter
0N/A */
0N/A public Object clone() throws CloneNotSupportedException {
0N/A DefaultFormatter formatter = (DefaultFormatter)super.clone();
0N/A
0N/A formatter.navigationFilter = null;
0N/A formatter.documentFilter = null;
0N/A formatter.replaceHolder = null;
0N/A return formatter;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Positions the cursor at the initial location.
0N/A */
0N/A void positionCursorAtInitialLocation() {
0N/A JFormattedTextField ftf = getFormattedTextField();
0N/A if (ftf != null) {
0N/A ftf.setCaretPosition(getInitialVisualPosition());
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the initial location to position the cursor at. This forwards
0N/A * the call to <code>getNextNavigatableChar</code>.
0N/A */
0N/A int getInitialVisualPosition() {
0N/A return getNextNavigatableChar(0, 1);
0N/A }
0N/A
0N/A /**
0N/A * Subclasses should override this if they want cursor navigation
0N/A * to skip certain characters. A return value of false indicates
0N/A * the character at <code>offset</code> should be skipped when
0N/A * navigating throught the field.
0N/A */
0N/A boolean isNavigatable(int offset) {
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the text in <code>text</code> can be inserted. This
0N/A * does not mean the text will ultimately be inserted, it is used if
0N/A * text can trivially reject certain characters.
0N/A */
0N/A boolean isLegalInsertText(String text) {
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Returns the next editable character starting at offset incrementing
0N/A * the offset by <code>direction</code>.
0N/A */
0N/A private int getNextNavigatableChar(int offset, int direction) {
0N/A int max = getFormattedTextField().getDocument().getLength();
0N/A
0N/A while (offset >= 0 && offset < max) {
0N/A if (isNavigatable(offset)) {
0N/A return offset;
0N/A }
0N/A offset += direction;
0N/A }
0N/A return offset;
0N/A }
0N/A
0N/A /**
0N/A * A convenience methods to return the result of deleting
0N/A * <code>deleteLength</code> characters at <code>offset</code>
0N/A * and inserting <code>replaceString</code> at <code>offset</code>
0N/A * in the current text field.
0N/A */
0N/A String getReplaceString(int offset, int deleteLength,
0N/A String replaceString) {
0N/A String string = getFormattedTextField().getText();
0N/A String result;
0N/A
0N/A result = string.substring(0, offset);
0N/A if (replaceString != null) {
0N/A result += replaceString;
0N/A }
0N/A if (offset + deleteLength < string.length()) {
0N/A result += string.substring(offset + deleteLength);
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A /*
0N/A * Returns true if the operation described by <code>rh</code> will
0N/A * result in a legal edit. This may set the <code>value</code>
0N/A * field of <code>rh</code>.
0N/A */
0N/A boolean isValidEdit(ReplaceHolder rh) {
0N/A if (!getAllowsInvalid()) {
0N/A String newString = getReplaceString(rh.offset, rh.length, rh.text);
0N/A
0N/A try {
0N/A rh.value = stringToValue(newString);
0N/A
0N/A return true;
0N/A } catch (ParseException pe) {
0N/A return false;
0N/A }
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Invokes <code>commitEdit</code> on the JFormattedTextField.
0N/A */
0N/A void commitEdit() throws ParseException {
0N/A JFormattedTextField ftf = getFormattedTextField();
0N/A
0N/A if (ftf != null) {
0N/A ftf.commitEdit();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Pushes the value to the JFormattedTextField if the current value
0N/A * is valid and invokes <code>setEditValid</code> based on the
0N/A * validity of the value.
0N/A */
0N/A void updateValue() {
0N/A updateValue(null);
0N/A }
0N/A
0N/A /**
0N/A * Pushes the <code>value</code> to the editor if we are to
0N/A * commit on edits. If <code>value</code> is null, the current value
0N/A * will be obtained from the text component.
0N/A */
0N/A void updateValue(Object value) {
0N/A try {
0N/A if (value == null) {
0N/A String string = getFormattedTextField().getText();
0N/A
0N/A value = stringToValue(string);
0N/A }
0N/A
0N/A if (getCommitsOnValidEdit()) {
0N/A commitEdit();
0N/A }
0N/A setEditValid(true);
0N/A } catch (ParseException pe) {
0N/A setEditValid(false);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the next cursor position from offset by incrementing
0N/A * <code>direction</code>. This uses
0N/A * <code>getNextNavigatableChar</code>
0N/A * as well as constraining the location to the max position.
0N/A */
0N/A int getNextCursorPosition(int offset, int direction) {
0N/A int newOffset = getNextNavigatableChar(offset, direction);
0N/A int max = getFormattedTextField().getDocument().getLength();
0N/A
0N/A if (!getAllowsInvalid()) {
0N/A if (direction == -1 && offset == newOffset) {
0N/A // Case where hit backspace and only characters before
0N/A // offset are fixed.
0N/A newOffset = getNextNavigatableChar(newOffset, 1);
0N/A if (newOffset >= max) {
0N/A newOffset = offset;
0N/A }
0N/A }
0N/A else if (direction == 1 && newOffset >= max) {
0N/A // Don't go beyond last editable character.
0N/A newOffset = getNextNavigatableChar(max - 1, -1);
0N/A if (newOffset < max) {
0N/A newOffset++;
0N/A }
0N/A }
0N/A }
0N/A return newOffset;
0N/A }
0N/A
0N/A /**
0N/A * Resets the cursor by using getNextCursorPosition.
0N/A */
0N/A void repositionCursor(int offset, int direction) {
0N/A getFormattedTextField().getCaret().setDot(getNextCursorPosition
0N/A (offset, direction));
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Finds the next navigatable character.
0N/A */
0N/A int getNextVisualPositionFrom(JTextComponent text, int pos,
0N/A Position.Bias bias, int direction,
0N/A Position.Bias[] biasRet)
0N/A throws BadLocationException {
0N/A int value = text.getUI().getNextVisualPositionFrom(text, pos, bias,
0N/A direction, biasRet);
0N/A
0N/A if (value == -1) {
0N/A return -1;
0N/A }
0N/A if (!getAllowsInvalid() && (direction == SwingConstants.EAST ||
0N/A direction == SwingConstants.WEST)) {
0N/A int last = -1;
0N/A
0N/A while (!isNavigatable(value) && value != last) {
0N/A last = value;
0N/A value = text.getUI().getNextVisualPositionFrom(
0N/A text, value, bias, direction,biasRet);
0N/A }
0N/A int max = getFormattedTextField().getDocument().getLength();
0N/A if (last == value || value == max) {
0N/A if (value == 0) {
0N/A biasRet[0] = Position.Bias.Forward;
0N/A value = getInitialVisualPosition();
0N/A }
0N/A if (value >= max && max > 0) {
0N/A // Pending: should not assume forward!
0N/A biasRet[0] = Position.Bias.Forward;
0N/A value = getNextNavigatableChar(max - 1, -1) + 1;
0N/A }
0N/A }
0N/A }
0N/A return value;
0N/A }
0N/A
0N/A /**
0N/A * Returns true if the edit described by <code>rh</code> will result
0N/A * in a legal value.
0N/A */
0N/A boolean canReplace(ReplaceHolder rh) {
0N/A return isValidEdit(rh);
0N/A }
0N/A
0N/A /**
0N/A * DocumentFilter method, funnels into <code>replace</code>.
0N/A */
0N/A void replace(DocumentFilter.FilterBypass fb, int offset,
0N/A int length, String text,
0N/A AttributeSet attrs) throws BadLocationException {
0N/A ReplaceHolder rh = getReplaceHolder(fb, offset, length, text, attrs);
0N/A
0N/A replace(rh);
0N/A }
0N/A
0N/A /**
0N/A * If the edit described by <code>rh</code> is legal, this will
0N/A * return true, commit the edit (if necessary) and update the cursor
0N/A * position. This forwards to <code>canReplace</code> and
0N/A * <code>isLegalInsertText</code> as necessary to determine if
0N/A * the edit is in fact legal.
0N/A * <p>
0N/A * All of the DocumentFilter methods funnel into here, you should
0N/A * generally only have to override this.
0N/A */
0N/A boolean replace(ReplaceHolder rh) throws BadLocationException {
0N/A boolean valid = true;
0N/A int direction = 1;
0N/A
0N/A if (rh.length > 0 && (rh.text == null || rh.text.length() == 0) &&
0N/A (getFormattedTextField().getSelectionStart() != rh.offset ||
0N/A rh.length > 1)) {
0N/A direction = -1;
0N/A }
0N/A
2599N/A if (getOverwriteMode() && rh.text != null &&
2599N/A getFormattedTextField().getSelectedText() == null)
2599N/A {
0N/A rh.length = Math.min(Math.max(rh.length, rh.text.length()),
0N/A rh.fb.getDocument().getLength() - rh.offset);
0N/A }
0N/A if ((rh.text != null && !isLegalInsertText(rh.text)) ||
0N/A !canReplace(rh) ||
0N/A (rh.length == 0 && (rh.text == null || rh.text.length() == 0))) {
0N/A valid = false;
0N/A }
0N/A if (valid) {
0N/A int cursor = rh.cursorPosition;
0N/A
0N/A rh.fb.replace(rh.offset, rh.length, rh.text, rh.attrs);
0N/A if (cursor == -1) {
0N/A cursor = rh.offset;
0N/A if (direction == 1 && rh.text != null) {
0N/A cursor = rh.offset + rh.text.length();
0N/A }
0N/A }
0N/A updateValue(rh.value);
0N/A repositionCursor(cursor, direction);
0N/A return true;
0N/A }
0N/A else {
0N/A invalidEdit();
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * NavigationFilter method, subclasses that wish finer control should
0N/A * override this.
0N/A */
0N/A void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias){
0N/A fb.setDot(dot, bias);
0N/A }
0N/A
0N/A /**
0N/A * NavigationFilter method, subclasses that wish finer control should
0N/A * override this.
0N/A */
0N/A void moveDot(NavigationFilter.FilterBypass fb, int dot,
0N/A Position.Bias bias) {
0N/A fb.moveDot(dot, bias);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns the ReplaceHolder to track the replace of the specified
0N/A * text.
0N/A */
0N/A ReplaceHolder getReplaceHolder(DocumentFilter.FilterBypass fb, int offset,
0N/A int length, String text,
0N/A AttributeSet attrs) {
0N/A if (replaceHolder == null) {
0N/A replaceHolder = new ReplaceHolder();
0N/A }
0N/A replaceHolder.reset(fb, offset, length, text, attrs);
0N/A return replaceHolder;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * ReplaceHolder is used to track where insert/remove/replace is
0N/A * going to happen.
0N/A */
0N/A static class ReplaceHolder {
0N/A /** The FilterBypass that was passed to the DocumentFilter method. */
0N/A DocumentFilter.FilterBypass fb;
0N/A /** Offset where the remove/insert is going to occur. */
0N/A int offset;
0N/A /** Length of text to remove. */
0N/A int length;
0N/A /** The text to insert, may be null. */
0N/A String text;
0N/A /** AttributeSet to attach to text, may be null. */
0N/A AttributeSet attrs;
0N/A /** The resulting value, this may never be set. */
0N/A Object value;
0N/A /** Position the cursor should be adjusted from. If this is -1
0N/A * the cursor position will be adjusted based on the direction of
0N/A * the replace (-1: offset, 1: offset + text.length()), otherwise
0N/A * the cursor position is adusted from this position.
0N/A */
0N/A int cursorPosition;
0N/A
0N/A void reset(DocumentFilter.FilterBypass fb, int offset, int length,
0N/A String text, AttributeSet attrs) {
0N/A this.fb = fb;
0N/A this.offset = offset;
0N/A this.length = length;
0N/A this.text = text;
0N/A this.attrs = attrs;
0N/A this.value = null;
0N/A cursorPosition = -1;
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * NavigationFilter implementation that calls back to methods with
0N/A * same name in DefaultFormatter.
0N/A */
0N/A private class DefaultNavigationFilter extends NavigationFilter
0N/A implements Serializable {
0N/A public void setDot(FilterBypass fb, int dot, Position.Bias bias) {
0N/A JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
0N/A if (tc.composedTextExists()) {
0N/A // bypass the filter
0N/A fb.setDot(dot, bias);
0N/A } else {
0N/A DefaultFormatter.this.setDot(fb, dot, bias);
0N/A }
0N/A }
0N/A
0N/A public void moveDot(FilterBypass fb, int dot, Position.Bias bias) {
0N/A JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
0N/A if (tc.composedTextExists()) {
0N/A // bypass the filter
0N/A fb.moveDot(dot, bias);
0N/A } else {
0N/A DefaultFormatter.this.moveDot(fb, dot, bias);
0N/A }
0N/A }
0N/A
0N/A public int getNextVisualPositionFrom(JTextComponent text, int pos,
0N/A Position.Bias bias,
0N/A int direction,
0N/A Position.Bias[] biasRet)
0N/A throws BadLocationException {
0N/A if (text.composedTextExists()) {
0N/A // forward the call to the UI directly
0N/A return text.getUI().getNextVisualPositionFrom(
0N/A text, pos, bias, direction, biasRet);
0N/A } else {
0N/A return DefaultFormatter.this.getNextVisualPositionFrom(
0N/A text, pos, bias, direction, biasRet);
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * DocumentFilter implementation that calls back to the replace
0N/A * method of DefaultFormatter.
0N/A */
0N/A private class DefaultDocumentFilter extends DocumentFilter implements
0N/A Serializable {
0N/A public void remove(FilterBypass fb, int offset, int length) throws
0N/A BadLocationException {
0N/A JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
0N/A if (tc.composedTextExists()) {
0N/A // bypass the filter
0N/A fb.remove(offset, length);
0N/A } else {
0N/A DefaultFormatter.this.replace(fb, offset, length, null, null);
0N/A }
0N/A }
0N/A
0N/A public void insertString(FilterBypass fb, int offset,
0N/A String string, AttributeSet attr) throws
0N/A BadLocationException {
0N/A JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
0N/A if (tc.composedTextExists() ||
0N/A Utilities.isComposedTextAttributeDefined(attr)) {
0N/A // bypass the filter
0N/A fb.insertString(offset, string, attr);
0N/A } else {
0N/A DefaultFormatter.this.replace(fb, offset, 0, string, attr);
0N/A }
0N/A }
0N/A
0N/A public void replace(FilterBypass fb, int offset, int length,
0N/A String text, AttributeSet attr) throws
0N/A BadLocationException {
0N/A JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
0N/A if (tc.composedTextExists() ||
0N/A Utilities.isComposedTextAttributeDefined(attr)) {
0N/A // bypass the filter
0N/A fb.replace(offset, length, text, attr);
0N/A } else {
0N/A DefaultFormatter.this.replace(fb, offset, length, text, attr);
0N/A }
0N/A }
0N/A }
0N/A}