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