Home » openjdk-7 » javax » swing » text » [javadoc | source]

    1   /*
    2    * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   package javax.swing.text;
   26   
   27   import java.io.Serializable;
   28   import java.lang.reflect;
   29   import java.text.ParseException;
   30   import javax.swing;
   31   import javax.swing.text;
   32   
   33   /**
   34    * <code>DefaultFormatter</code> formats aribtrary objects. Formatting is done
   35    * by invoking the <code>toString</code> method. In order to convert the
   36    * value back to a String, your class must provide a constructor that
   37    * takes a String argument. If no single argument constructor that takes a
   38    * String is found, the returned value will be the String passed into
   39    * <code>stringToValue</code>.
   40    * <p>
   41    * Instances of <code>DefaultFormatter</code> can not be used in multiple
   42    * instances of <code>JFormattedTextField</code>. To obtain a copy of
   43    * an already configured <code>DefaultFormatter</code>, use the
   44    * <code>clone</code> method.
   45    * <p>
   46    * <strong>Warning:</strong>
   47    * Serialized objects of this class will not be compatible with
   48    * future Swing releases. The current serialization support is
   49    * appropriate for short term storage or RMI between applications running
   50    * the same version of Swing.  As of 1.4, support for long term storage
   51    * of all JavaBeans<sup><font size="-2">TM</font></sup>
   52    * has been added to the <code>java.beans</code> package.
   53    * Please see {@link java.beans.XMLEncoder}.
   54    *
   55    * @see javax.swing.JFormattedTextField.AbstractFormatter
   56    *
   57    * @since 1.4
   58    */
   59   public class DefaultFormatter extends JFormattedTextField.AbstractFormatter
   60                       implements Cloneable, Serializable {
   61       /** Indicates if the value being edited must match the mask. */
   62       private boolean allowsInvalid;
   63   
   64       /** If true, editing mode is in overwrite (or strikethough). */
   65       private boolean overwriteMode;
   66   
   67       /** If true, any time a valid edit happens commitEdit is invoked. */
   68       private boolean commitOnEdit;
   69   
   70       /** Class used to create new instances. */
   71       private Class<?> valueClass;
   72   
   73       /** NavigationFilter that forwards calls back to DefaultFormatter. */
   74       private NavigationFilter navigationFilter;
   75   
   76       /** DocumentFilter that forwards calls back to DefaultFormatter. */
   77       private DocumentFilter documentFilter;
   78   
   79       /** Used during replace to track the region to replace. */
   80       transient ReplaceHolder replaceHolder;
   81   
   82   
   83       /**
   84        * Creates a DefaultFormatter.
   85        */
   86       public DefaultFormatter() {
   87           overwriteMode = true;
   88           allowsInvalid = true;
   89       }
   90   
   91       /**
   92        * Installs the <code>DefaultFormatter</code> onto a particular
   93        * <code>JFormattedTextField</code>.
   94        * This will invoke <code>valueToString</code> to convert the
   95        * current value from the <code>JFormattedTextField</code> to
   96        * a String. This will then install the <code>Action</code>s from
   97        * <code>getActions</code>, the <code>DocumentFilter</code>
   98        * returned from <code>getDocumentFilter</code> and the
   99        * <code>NavigationFilter</code> returned from
  100        * <code>getNavigationFilter</code> onto the
  101        * <code>JFormattedTextField</code>.
  102        * <p>
  103        * Subclasses will typically only need to override this if they
  104        * wish to install additional listeners on the
  105        * <code>JFormattedTextField</code>.
  106        * <p>
  107        * If there is a <code>ParseException</code> in converting the
  108        * current value to a String, this will set the text to an empty
  109        * String, and mark the <code>JFormattedTextField</code> as being
  110        * in an invalid state.
  111        * <p>
  112        * While this is a public method, this is typically only useful
  113        * for subclassers of <code>JFormattedTextField</code>.
  114        * <code>JFormattedTextField</code> will invoke this method at
  115        * the appropriate times when the value changes, or its internal
  116        * state changes.
  117        *
  118        * @param ftf JFormattedTextField to format for, may be null indicating
  119        *            uninstall from current JFormattedTextField.
  120        */
  121       public void install(JFormattedTextField ftf) {
  122           super.install(ftf);
  123           positionCursorAtInitialLocation();
  124       }
  125   
  126       /**
  127        * Sets when edits are published back to the
  128        * <code>JFormattedTextField</code>. If true, <code>commitEdit</code>
  129        * is invoked after every valid edit (any time the text is edited). On
  130        * the other hand, if this is false than the <code>DefaultFormatter</code>
  131        * does not publish edits back to the <code>JFormattedTextField</code>.
  132        * As such, the only time the value of the <code>JFormattedTextField</code>
  133        * will change is when <code>commitEdit</code> is invoked on
  134        * <code>JFormattedTextField</code>, typically when enter is pressed
  135        * or focus leaves the <code>JFormattedTextField</code>.
  136        *
  137        * @param commit Used to indicate when edits are commited back to the
  138        *               JTextComponent
  139        */
  140       public void setCommitsOnValidEdit(boolean commit) {
  141           commitOnEdit = commit;
  142       }
  143   
  144       /**
  145        * Returns when edits are published back to the
  146        * <code>JFormattedTextField</code>.
  147        *
  148        * @return true if edits are commited after evey valid edit
  149        */
  150       public boolean getCommitsOnValidEdit() {
  151           return commitOnEdit;
  152       }
  153   
  154       /**
  155        * Configures the behavior when inserting characters. If
  156        * <code>overwriteMode</code> is true (the default), new characters
  157        * overwrite existing characters in the model.
  158        *
  159        * @param overwriteMode Indicates if overwrite or overstrike mode is used
  160        */
  161       public void setOverwriteMode(boolean overwriteMode) {
  162           this.overwriteMode = overwriteMode;
  163       }
  164   
  165       /**
  166        * Returns the behavior when inserting characters.
  167        *
  168        * @return true if newly inserted characters overwrite existing characters
  169        */
  170       public boolean getOverwriteMode() {
  171           return overwriteMode;
  172       }
  173   
  174       /**
  175        * Sets whether or not the value being edited is allowed to be invalid
  176        * for a length of time (that is, <code>stringToValue</code> throws
  177        * a <code>ParseException</code>).
  178        * It is often convenient to allow the user to temporarily input an
  179        * invalid value.
  180        *
  181        * @param allowsInvalid Used to indicate if the edited value must always
  182        *        be valid
  183        */
  184       public void setAllowsInvalid(boolean allowsInvalid) {
  185           this.allowsInvalid = allowsInvalid;
  186       }
  187   
  188       /**
  189        * Returns whether or not the value being edited is allowed to be invalid
  190        * for a length of time.
  191        *
  192        * @return false if the edited value must always be valid
  193        */
  194       public boolean getAllowsInvalid() {
  195           return allowsInvalid;
  196       }
  197   
  198       /**
  199        * Sets that class that is used to create new Objects. If the
  200        * passed in class does not have a single argument constructor that
  201        * takes a String, String values will be used.
  202        *
  203        * @param valueClass Class used to construct return value from
  204        *        stringToValue
  205        */
  206       public void setValueClass(Class<?> valueClass) {
  207           this.valueClass = valueClass;
  208       }
  209   
  210       /**
  211        * Returns that class that is used to create new Objects.
  212        *
  213        * @return Class used to constuct return value from stringToValue
  214        */
  215       public Class<?> getValueClass() {
  216           return valueClass;
  217       }
  218   
  219       /**
  220        * Converts the passed in String into an instance of
  221        * <code>getValueClass</code> by way of the constructor that
  222        * takes a String argument. If <code>getValueClass</code>
  223        * returns null, the Class of the current value in the
  224        * <code>JFormattedTextField</code> will be used. If this is null, a
  225        * String will be returned. If the constructor thows an exception, a
  226        * <code>ParseException</code> will be thrown. If there is no single
  227        * argument String constructor, <code>string</code> will be returned.
  228        *
  229        * @throws ParseException if there is an error in the conversion
  230        * @param string String to convert
  231        * @return Object representation of text
  232        */
  233       public Object stringToValue(String string) throws ParseException {
  234           Class<?> vc = getValueClass();
  235           JFormattedTextField ftf = getFormattedTextField();
  236   
  237           if (vc == null && ftf != null) {
  238               Object value = ftf.getValue();
  239   
  240               if (value != null) {
  241                   vc = value.getClass();
  242               }
  243           }
  244           if (vc != null) {
  245               Constructor cons;
  246   
  247               try {
  248                   cons = vc.getConstructor(new Class[] { String.class });
  249   
  250               } catch (NoSuchMethodException nsme) {
  251                   cons = null;
  252               }
  253   
  254               if (cons != null) {
  255                   try {
  256                       return cons.newInstance(new Object[] { string });
  257                   } catch (Throwable ex) {
  258                       throw new ParseException("Error creating instance", 0);
  259                   }
  260               }
  261           }
  262           return string;
  263       }
  264   
  265       /**
  266        * Converts the passed in Object into a String by way of the
  267        * <code>toString</code> method.
  268        *
  269        * @throws ParseException if there is an error in the conversion
  270        * @param value Value to convert
  271        * @return String representation of value
  272        */
  273       public String valueToString(Object value) throws ParseException {
  274           if (value == null) {
  275               return "";
  276           }
  277           return value.toString();
  278       }
  279   
  280       /**
  281        * Returns the <code>DocumentFilter</code> used to restrict the characters
  282        * that can be input into the <code>JFormattedTextField</code>.
  283        *
  284        * @return DocumentFilter to restrict edits
  285        */
  286       protected DocumentFilter getDocumentFilter() {
  287           if (documentFilter == null) {
  288               documentFilter = new DefaultDocumentFilter();
  289           }
  290           return documentFilter;
  291       }
  292   
  293       /**
  294        * Returns the <code>NavigationFilter</code> used to restrict where the
  295        * cursor can be placed.
  296        *
  297        * @return NavigationFilter to restrict navigation
  298        */
  299       protected NavigationFilter getNavigationFilter() {
  300           if (navigationFilter == null) {
  301               navigationFilter = new DefaultNavigationFilter();
  302           }
  303           return navigationFilter;
  304       }
  305   
  306       /**
  307        * Creates a copy of the DefaultFormatter.
  308        *
  309        * @return copy of the DefaultFormatter
  310        */
  311       public Object clone() throws CloneNotSupportedException {
  312           DefaultFormatter formatter = (DefaultFormatter)super.clone();
  313   
  314           formatter.navigationFilter = null;
  315           formatter.documentFilter = null;
  316           formatter.replaceHolder = null;
  317           return formatter;
  318       }
  319   
  320   
  321       /**
  322        * Positions the cursor at the initial location.
  323        */
  324       void positionCursorAtInitialLocation() {
  325           JFormattedTextField ftf = getFormattedTextField();
  326           if (ftf != null) {
  327               ftf.setCaretPosition(getInitialVisualPosition());
  328           }
  329       }
  330   
  331       /**
  332        * Returns the initial location to position the cursor at. This forwards
  333        * the call to <code>getNextNavigatableChar</code>.
  334        */
  335       int getInitialVisualPosition() {
  336           return getNextNavigatableChar(0, 1);
  337       }
  338   
  339       /**
  340        * Subclasses should override this if they want cursor navigation
  341        * to skip certain characters. A return value of false indicates
  342        * the character at <code>offset</code> should be skipped when
  343        * navigating throught the field.
  344        */
  345       boolean isNavigatable(int offset) {
  346           return true;
  347       }
  348   
  349       /**
  350        * Returns true if the text in <code>text</code> can be inserted.  This
  351        * does not mean the text will ultimately be inserted, it is used if
  352        * text can trivially reject certain characters.
  353        */
  354       boolean isLegalInsertText(String text) {
  355           return true;
  356       }
  357   
  358       /**
  359        * Returns the next editable character starting at offset incrementing
  360        * the offset by <code>direction</code>.
  361        */
  362       private int getNextNavigatableChar(int offset, int direction) {
  363           int max = getFormattedTextField().getDocument().getLength();
  364   
  365           while (offset >= 0 && offset < max) {
  366               if (isNavigatable(offset)) {
  367                   return offset;
  368               }
  369               offset += direction;
  370           }
  371           return offset;
  372       }
  373   
  374       /**
  375        * A convenience methods to return the result of deleting
  376        * <code>deleteLength</code> characters at <code>offset</code>
  377        * and inserting <code>replaceString</code> at <code>offset</code>
  378        * in the current text field.
  379        */
  380       String getReplaceString(int offset, int deleteLength,
  381                               String replaceString) {
  382           String string = getFormattedTextField().getText();
  383           String result;
  384   
  385           result = string.substring(0, offset);
  386           if (replaceString != null) {
  387               result += replaceString;
  388           }
  389           if (offset + deleteLength < string.length()) {
  390               result += string.substring(offset + deleteLength);
  391           }
  392           return result;
  393       }
  394   
  395       /*
  396        * Returns true if the operation described by <code>rh</code> will
  397        * result in a legal edit.  This may set the <code>value</code>
  398        * field of <code>rh</code>.
  399        */
  400       boolean isValidEdit(ReplaceHolder rh) {
  401           if (!getAllowsInvalid()) {
  402               String newString = getReplaceString(rh.offset, rh.length, rh.text);
  403   
  404               try {
  405                   rh.value = stringToValue(newString);
  406   
  407                   return true;
  408               } catch (ParseException pe) {
  409                   return false;
  410               }
  411           }
  412           return true;
  413       }
  414   
  415       /**
  416        * Invokes <code>commitEdit</code> on the JFormattedTextField.
  417        */
  418       void commitEdit() throws ParseException {
  419           JFormattedTextField ftf = getFormattedTextField();
  420   
  421           if (ftf != null) {
  422               ftf.commitEdit();
  423           }
  424       }
  425   
  426       /**
  427        * Pushes the value to the JFormattedTextField if the current value
  428        * is valid and invokes <code>setEditValid</code> based on the
  429        * validity of the value.
  430        */
  431       void updateValue() {
  432           updateValue(null);
  433       }
  434   
  435       /**
  436        * Pushes the <code>value</code> to the editor if we are to
  437        * commit on edits. If <code>value</code> is null, the current value
  438        * will be obtained from the text component.
  439        */
  440       void updateValue(Object value) {
  441           try {
  442               if (value == null) {
  443                   String string = getFormattedTextField().getText();
  444   
  445                   value = stringToValue(string);
  446               }
  447   
  448               if (getCommitsOnValidEdit()) {
  449                   commitEdit();
  450               }
  451               setEditValid(true);
  452           } catch (ParseException pe) {
  453               setEditValid(false);
  454           }
  455       }
  456   
  457       /**
  458        * Returns the next cursor position from offset by incrementing
  459        * <code>direction</code>. This uses
  460        * <code>getNextNavigatableChar</code>
  461        * as well as constraining the location to the max position.
  462        */
  463       int getNextCursorPosition(int offset, int direction) {
  464           int newOffset = getNextNavigatableChar(offset, direction);
  465           int max = getFormattedTextField().getDocument().getLength();
  466   
  467           if (!getAllowsInvalid()) {
  468               if (direction == -1 && offset == newOffset) {
  469                   // Case where hit backspace and only characters before
  470                   // offset are fixed.
  471                   newOffset = getNextNavigatableChar(newOffset, 1);
  472                   if (newOffset >= max) {
  473                       newOffset = offset;
  474                   }
  475               }
  476               else if (direction == 1 && newOffset >= max) {
  477                   // Don't go beyond last editable character.
  478                   newOffset = getNextNavigatableChar(max - 1, -1);
  479                   if (newOffset < max) {
  480                       newOffset++;
  481                   }
  482               }
  483           }
  484           return newOffset;
  485       }
  486   
  487       /**
  488        * Resets the cursor by using getNextCursorPosition.
  489        */
  490       void repositionCursor(int offset, int direction) {
  491           getFormattedTextField().getCaret().setDot(getNextCursorPosition
  492                                                     (offset, direction));
  493       }
  494   
  495   
  496       /**
  497        * Finds the next navigatable character.
  498        */
  499       int getNextVisualPositionFrom(JTextComponent text, int pos,
  500                                     Position.Bias bias, int direction,
  501                                     Position.Bias[] biasRet)
  502                                              throws BadLocationException {
  503           int value = text.getUI().getNextVisualPositionFrom(text, pos, bias,
  504                                                              direction, biasRet);
  505   
  506           if (value == -1) {
  507               return -1;
  508           }
  509           if (!getAllowsInvalid() && (direction == SwingConstants.EAST ||
  510                                       direction == SwingConstants.WEST)) {
  511               int last = -1;
  512   
  513               while (!isNavigatable(value) && value != last) {
  514                   last = value;
  515                   value = text.getUI().getNextVisualPositionFrom(
  516                                 text, value, bias, direction,biasRet);
  517               }
  518               int max = getFormattedTextField().getDocument().getLength();
  519               if (last == value || value == max) {
  520                   if (value == 0) {
  521                       biasRet[0] = Position.Bias.Forward;
  522                       value = getInitialVisualPosition();
  523                   }
  524                   if (value >= max && max > 0) {
  525                       // Pending: should not assume forward!
  526                       biasRet[0] = Position.Bias.Forward;
  527                       value = getNextNavigatableChar(max - 1, -1) + 1;
  528                   }
  529               }
  530           }
  531           return value;
  532       }
  533   
  534       /**
  535        * Returns true if the edit described by <code>rh</code> will result
  536        * in a legal value.
  537        */
  538       boolean canReplace(ReplaceHolder rh) {
  539           return isValidEdit(rh);
  540       }
  541   
  542       /**
  543        * DocumentFilter method, funnels into <code>replace</code>.
  544        */
  545       void replace(DocumentFilter.FilterBypass fb, int offset,
  546                        int length, String text,
  547                        AttributeSet attrs) throws BadLocationException {
  548           ReplaceHolder rh = getReplaceHolder(fb, offset, length, text, attrs);
  549   
  550           replace(rh);
  551       }
  552   
  553       /**
  554        * If the edit described by <code>rh</code> is legal, this will
  555        * return true, commit the edit (if necessary) and update the cursor
  556        * position.  This forwards to <code>canReplace</code> and
  557        * <code>isLegalInsertText</code> as necessary to determine if
  558        * the edit is in fact legal.
  559        * <p>
  560        * All of the DocumentFilter methods funnel into here, you should
  561        * generally only have to override this.
  562        */
  563       boolean replace(ReplaceHolder rh) throws BadLocationException {
  564           boolean valid = true;
  565           int direction = 1;
  566   
  567           if (rh.length > 0 && (rh.text == null || rh.text.length() == 0) &&
  568                  (getFormattedTextField().getSelectionStart() != rh.offset ||
  569                      rh.length > 1)) {
  570               direction = -1;
  571           }
  572   
  573           if (getOverwriteMode() && rh.text != null &&
  574               getFormattedTextField().getSelectedText() == null)
  575           {
  576               rh.length = Math.min(Math.max(rh.length, rh.text.length()),
  577                                    rh.fb.getDocument().getLength() - rh.offset);
  578           }
  579           if ((rh.text != null && !isLegalInsertText(rh.text)) ||
  580               !canReplace(rh) ||
  581               (rh.length == 0 && (rh.text == null || rh.text.length() == 0))) {
  582               valid = false;
  583           }
  584           if (valid) {
  585               int cursor = rh.cursorPosition;
  586   
  587               rh.fb.replace(rh.offset, rh.length, rh.text, rh.attrs);
  588               if (cursor == -1) {
  589                   cursor = rh.offset;
  590                   if (direction == 1 && rh.text != null) {
  591                       cursor = rh.offset + rh.text.length();
  592                   }
  593               }
  594               updateValue(rh.value);
  595               repositionCursor(cursor, direction);
  596               return true;
  597           }
  598           else {
  599               invalidEdit();
  600           }
  601           return false;
  602       }
  603   
  604       /**
  605        * NavigationFilter method, subclasses that wish finer control should
  606        * override this.
  607        */
  608       void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias){
  609           fb.setDot(dot, bias);
  610       }
  611   
  612       /**
  613        * NavigationFilter method, subclasses that wish finer control should
  614        * override this.
  615        */
  616       void moveDot(NavigationFilter.FilterBypass fb, int dot,
  617                    Position.Bias bias) {
  618           fb.moveDot(dot, bias);
  619       }
  620   
  621   
  622       /**
  623        * Returns the ReplaceHolder to track the replace of the specified
  624        * text.
  625        */
  626       ReplaceHolder getReplaceHolder(DocumentFilter.FilterBypass fb, int offset,
  627                                      int length, String text,
  628                                      AttributeSet attrs) {
  629           if (replaceHolder == null) {
  630               replaceHolder = new ReplaceHolder();
  631           }
  632           replaceHolder.reset(fb, offset, length, text, attrs);
  633           return replaceHolder;
  634       }
  635   
  636   
  637       /**
  638        * ReplaceHolder is used to track where insert/remove/replace is
  639        * going to happen.
  640        */
  641       static class ReplaceHolder {
  642           /** The FilterBypass that was passed to the DocumentFilter method. */
  643           DocumentFilter.FilterBypass fb;
  644           /** Offset where the remove/insert is going to occur. */
  645           int offset;
  646           /** Length of text to remove. */
  647           int length;
  648           /** The text to insert, may be null. */
  649           String text;
  650           /** AttributeSet to attach to text, may be null. */
  651           AttributeSet attrs;
  652           /** The resulting value, this may never be set. */
  653           Object value;
  654           /** Position the cursor should be adjusted from.  If this is -1
  655            * the cursor position will be adjusted based on the direction of
  656            * the replace (-1: offset, 1: offset + text.length()), otherwise
  657            * the cursor position is adusted from this position.
  658            */
  659           int cursorPosition;
  660   
  661           void reset(DocumentFilter.FilterBypass fb, int offset, int length,
  662                      String text, AttributeSet attrs) {
  663               this.fb = fb;
  664               this.offset = offset;
  665               this.length = length;
  666               this.text = text;
  667               this.attrs = attrs;
  668               this.value = null;
  669               cursorPosition = -1;
  670           }
  671       }
  672   
  673   
  674       /**
  675        * NavigationFilter implementation that calls back to methods with
  676        * same name in DefaultFormatter.
  677        */
  678       private class DefaultNavigationFilter extends NavigationFilter
  679                                implements Serializable {
  680           public void setDot(FilterBypass fb, int dot, Position.Bias bias) {
  681               JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
  682               if (tc.composedTextExists()) {
  683                   // bypass the filter
  684                   fb.setDot(dot, bias);
  685               } else {
  686                   DefaultFormatter.this.setDot(fb, dot, bias);
  687               }
  688           }
  689   
  690           public void moveDot(FilterBypass fb, int dot, Position.Bias bias) {
  691               JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
  692               if (tc.composedTextExists()) {
  693                   // bypass the filter
  694                   fb.moveDot(dot, bias);
  695               } else {
  696                   DefaultFormatter.this.moveDot(fb, dot, bias);
  697               }
  698           }
  699   
  700           public int getNextVisualPositionFrom(JTextComponent text, int pos,
  701                                                Position.Bias bias,
  702                                                int direction,
  703                                                Position.Bias[] biasRet)
  704                                              throws BadLocationException {
  705               if (text.composedTextExists()) {
  706                   // forward the call to the UI directly
  707                   return text.getUI().getNextVisualPositionFrom(
  708                           text, pos, bias, direction, biasRet);
  709               } else {
  710                   return DefaultFormatter.this.getNextVisualPositionFrom(
  711                           text, pos, bias, direction, biasRet);
  712               }
  713           }
  714       }
  715   
  716   
  717       /**
  718        * DocumentFilter implementation that calls back to the replace
  719        * method of DefaultFormatter.
  720        */
  721       private class DefaultDocumentFilter extends DocumentFilter implements
  722                                Serializable {
  723           public void remove(FilterBypass fb, int offset, int length) throws
  724                                 BadLocationException {
  725               JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
  726               if (tc.composedTextExists()) {
  727                   // bypass the filter
  728                   fb.remove(offset, length);
  729               } else {
  730                   DefaultFormatter.this.replace(fb, offset, length, null, null);
  731               }
  732           }
  733   
  734           public void insertString(FilterBypass fb, int offset,
  735                                    String string, AttributeSet attr) throws
  736                                 BadLocationException {
  737               JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
  738               if (tc.composedTextExists() ||
  739                   Utilities.isComposedTextAttributeDefined(attr)) {
  740                   // bypass the filter
  741                   fb.insertString(offset, string, attr);
  742               } else {
  743                   DefaultFormatter.this.replace(fb, offset, 0, string, attr);
  744               }
  745           }
  746   
  747           public void replace(FilterBypass fb, int offset, int length,
  748                                    String text, AttributeSet attr) throws
  749                                 BadLocationException {
  750               JTextComponent tc = DefaultFormatter.this.getFormattedTextField();
  751               if (tc.composedTextExists() ||
  752                   Utilities.isComposedTextAttributeDefined(attr)) {
  753                   // bypass the filter
  754                   fb.replace(offset, length, text, attr);
  755               } else {
  756                   DefaultFormatter.this.replace(fb, offset, length, text, attr);
  757               }
  758           }
  759       }
  760   }

Home » openjdk-7 » javax » swing » text » [javadoc | source]