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

    1   /*
    2    * Copyright (c) 2000, 2006, 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;
   26   
   27   import java.awt;
   28   import java.awt.event;
   29   import java.awt.im.InputContext;
   30   import java.io;
   31   import java.text;
   32   import java.util;
   33   import javax.swing.UIManager;
   34   import javax.swing.event;
   35   import javax.swing.plaf.UIResource;
   36   import javax.swing.text;
   37   
   38   /**
   39    * <code>JFormattedTextField</code> extends <code>JTextField</code> adding
   40    * support for formatting arbitrary values, as well as retrieving a particular
   41    * object once the user has edited the text. The following illustrates
   42    * configuring a <code>JFormattedTextField</code> to edit dates:
   43    * <pre>
   44    *   JFormattedTextField ftf = new JFormattedTextField();
   45    *   ftf.setValue(new Date());
   46    * </pre>
   47    * <p>
   48    * Once a <code>JFormattedTextField</code> has been created, you can
   49    * listen for editing changes by way of adding
   50    * a <code>PropertyChangeListener</code> and listening for
   51    * <code>PropertyChangeEvent</code>s with the property name <code>value</code>.
   52    * <p>
   53    * <code>JFormattedTextField</code> allows
   54    * configuring what action should be taken when focus is lost. The possible
   55    * configurations are:
   56    * <table summary="Possible JFormattedTextField configurations and their descriptions">
   57    * <tr><th><p align="left">Value</p></th><th><p align="left">Description</p></th></tr>
   58    * <tr><td>JFormattedTextField.REVERT
   59    *            <td>Revert the display to match that of <code>getValue</code>,
   60    *                possibly losing the current edit.
   61    *        <tr><td>JFormattedTextField.COMMIT
   62    *            <td>Commits the current value. If the value being edited
   63    *                isn't considered a legal value by the
   64    *                <code>AbstractFormatter</code> that is, a
   65    *                <code>ParseException</code> is thrown, then the value
   66    *                will not change, and then edited value will persist.
   67    *        <tr><td>JFormattedTextField.COMMIT_OR_REVERT
   68    *            <td>Similar to <code>COMMIT</code>, but if the value isn't
   69    *                legal, behave like <code>REVERT</code>.
   70    *        <tr><td>JFormattedTextField.PERSIST
   71    *            <td>Do nothing, don't obtain a new
   72    *                <code>AbstractFormatter</code>, and don't update the value.
   73    * </table>
   74    * The default is <code>JFormattedTextField.COMMIT_OR_REVERT</code>,
   75    * refer to {@link #setFocusLostBehavior} for more information on this.
   76    * <p>
   77    * <code>JFormattedTextField</code> allows the focus to leave, even if
   78    * the currently edited value is invalid. To lock the focus down while the
   79    * <code>JFormattedTextField</code> is an invalid edit state
   80    * you can attach an <code>InputVerifier</code>. The following code snippet
   81    * shows a potential implementation of such an <code>InputVerifier</code>:
   82    * <pre>
   83    * public class FormattedTextFieldVerifier extends InputVerifier {
   84    *     public boolean verify(JComponent input) {
   85    *         if (input instanceof JFormattedTextField) {
   86    *             JFormattedTextField ftf = (JFormattedTextField)input;
   87    *             AbstractFormatter formatter = ftf.getFormatter();
   88    *             if (formatter != null) {
   89    *                 String text = ftf.getText();
   90    *                 try {
   91    *                      formatter.stringToValue(text);
   92    *                      return true;
   93    *                  } catch (ParseException pe) {
   94    *                      return false;
   95    *                  }
   96    *              }
   97    *          }
   98    *          return true;
   99    *      }
  100    *      public boolean shouldYieldFocus(JComponent input) {
  101    *          return verify(input);
  102    *      }
  103    *  }
  104    * </pre>
  105    * <p>
  106    * Alternatively, you could invoke <code>commitEdit</code>, which would also
  107    * commit the value.
  108    * <p>
  109    * <code>JFormattedTextField</code> does not do the formatting it self,
  110    * rather formatting is done through an instance of
  111    * <code>JFormattedTextField.AbstractFormatter</code> which is obtained from
  112    * an instance of <code>JFormattedTextField.AbstractFormatterFactory</code>.
  113    * Instances of <code>JFormattedTextField.AbstractFormatter</code> are
  114    * notified when they become active by way of the
  115    * <code>install</code> method, at which point the
  116    * <code>JFormattedTextField.AbstractFormatter</code> can install whatever
  117    * it needs to, typically a <code>DocumentFilter</code>. Similarly when
  118    * <code>JFormattedTextField</code> no longer
  119    * needs the <code>AbstractFormatter</code>, it will invoke
  120    * <code>uninstall</code>.
  121    * <p>
  122    * <code>JFormattedTextField</code> typically
  123    * queries the <code>AbstractFormatterFactory</code> for an
  124    * <code>AbstractFormat</code> when it gains or loses focus. Although this
  125    * can change based on the focus lost policy. If the focus lost
  126    * policy is <code>JFormattedTextField.PERSIST</code>
  127    * and the <code>JFormattedTextField</code> has been edited, the
  128    * <code>AbstractFormatterFactory</code> will not be queried until the
  129    * value has been commited. Similarly if the focus lost policy is
  130    * <code>JFormattedTextField.COMMIT</code> and an exception
  131    * is thrown from <code>stringToValue</code>, the
  132    * <code>AbstractFormatterFactory</code> will not be querired when focus is
  133    * lost or gained.
  134    * <p>
  135    * <code>JFormattedTextField.AbstractFormatter</code>
  136    * is also responsible for determining when values are commited to
  137    * the <code>JFormattedTextField</code>. Some
  138    * <code>JFormattedTextField.AbstractFormatter</code>s will make new values
  139    * available on every edit, and others will never commit the value. You can
  140    * force the current value to be obtained
  141    * from the current <code>JFormattedTextField.AbstractFormatter</code>
  142    * by way of invoking <code>commitEdit</code>. <code>commitEdit</code> will
  143    * be invoked whenever return is pressed in the
  144    * <code>JFormattedTextField</code>.
  145    * <p>
  146    * If an <code>AbstractFormatterFactory</code> has not been explicitly
  147    * set, one will be set based on the <code>Class</code> of the value type after
  148    * <code>setValue</code> has been invoked (assuming value is non-null).
  149    * For example, in the following code an appropriate
  150    * <code>AbstractFormatterFactory</code> and <code>AbstractFormatter</code>
  151    * will be created to handle formatting of numbers:
  152    * <pre>
  153    *   JFormattedTextField tf = new JFormattedTextField();
  154    *   tf.setValue(new Number(100));
  155    * </pre>
  156    * <p>
  157    * <strong>Warning:</strong> As the <code>AbstractFormatter</code> will
  158    * typically install a <code>DocumentFilter</code> on the
  159    * <code>Document</code>, and a <code>NavigationFilter</code> on the
  160    * <code>JFormattedTextField</code> you should not install your own. If you do,
  161    * you are likely to see odd behavior in that the editing policy of the
  162    * <code>AbstractFormatter</code> will not be enforced.
  163    * <p>
  164    * <strong>Warning:</strong> Swing is not thread safe. For more
  165    * information see <a
  166    * href="package-summary.html#threading">Swing's Threading
  167    * Policy</a>.
  168    * <p>
  169    * <strong>Warning:</strong>
  170    * Serialized objects of this class will not be compatible with
  171    * future Swing releases. The current serialization support is
  172    * appropriate for short term storage or RMI between applications running
  173    * the same version of Swing.  As of 1.4, support for long term storage
  174    * of all JavaBeans<sup><font size="-2">TM</font></sup>
  175    * has been added to the <code>java.beans</code> package.
  176    * Please see {@link java.beans.XMLEncoder}.
  177    *
  178    * @since 1.4
  179    */
  180   public class JFormattedTextField extends JTextField {
  181       private static final String uiClassID = "FormattedTextFieldUI";
  182       private static final Action[] defaultActions =
  183               { new CommitAction(), new CancelAction() };
  184   
  185       /**
  186        * Constant identifying that when focus is lost,
  187        * <code>commitEdit</code> should be invoked. If in commiting the
  188        * new value a <code>ParseException</code> is thrown, the invalid
  189        * value will remain.
  190        *
  191        * @see #setFocusLostBehavior
  192        */
  193       public static final int COMMIT = 0;
  194   
  195       /**
  196        * Constant identifying that when focus is lost,
  197        * <code>commitEdit</code> should be invoked. If in commiting the new
  198        * value a <code>ParseException</code> is thrown, the value will be
  199        * reverted.
  200        *
  201        * @see #setFocusLostBehavior
  202        */
  203       public static final int COMMIT_OR_REVERT = 1;
  204   
  205       /**
  206        * Constant identifying that when focus is lost, editing value should
  207        * be reverted to current value set on the
  208        * <code>JFormattedTextField</code>.
  209        *
  210        * @see #setFocusLostBehavior
  211        */
  212       public static final int REVERT = 2;
  213   
  214       /**
  215        * Constant identifying that when focus is lost, the edited value
  216        * should be left.
  217        *
  218        * @see #setFocusLostBehavior
  219        */
  220       public static final int PERSIST = 3;
  221   
  222   
  223       /**
  224        * Factory used to obtain an instance of AbstractFormatter.
  225        */
  226       private AbstractFormatterFactory factory;
  227       /**
  228        * Object responsible for formatting the current value.
  229        */
  230       private AbstractFormatter format;
  231       /**
  232        * Last valid value.
  233        */
  234       private Object value;
  235       /**
  236        * True while the value being edited is valid.
  237        */
  238       private boolean editValid;
  239       /**
  240        * Behavior when focus is lost.
  241        */
  242       private int focusLostBehavior;
  243       /**
  244        * Indicates the current value has been edited.
  245        */
  246       private boolean edited;
  247       /**
  248        * Used to set the dirty state.
  249        */
  250       private DocumentListener documentListener;
  251       /**
  252        * Masked used to set the AbstractFormatterFactory.
  253        */
  254       private Object mask;
  255       /**
  256        * ActionMap that the TextFormatter Actions are added to.
  257        */
  258       private ActionMap textFormatterActionMap;
  259       /**
  260        * Indicates the input method composed text is in the document
  261        */
  262       private boolean composedTextExists = false;
  263       /**
  264        * A handler for FOCUS_LOST event
  265        */
  266       private FocusLostHandler focusLostHandler;
  267   
  268   
  269       /**
  270        * Creates a <code>JFormattedTextField</code> with no
  271        * <code>AbstractFormatterFactory</code>. Use <code>setMask</code> or
  272        * <code>setFormatterFactory</code> to configure the
  273        * <code>JFormattedTextField</code> to edit a particular type of
  274        * value.
  275        */
  276       public JFormattedTextField() {
  277           super();
  278           enableEvents(AWTEvent.FOCUS_EVENT_MASK);
  279           setFocusLostBehavior(COMMIT_OR_REVERT);
  280       }
  281   
  282       /**
  283        * Creates a JFormattedTextField with the specified value. This will
  284        * create an <code>AbstractFormatterFactory</code> based on the
  285        * type of <code>value</code>.
  286        *
  287        * @param value Initial value for the JFormattedTextField
  288        */
  289       public JFormattedTextField(Object value) {
  290           this();
  291           setValue(value);
  292       }
  293   
  294       /**
  295        * Creates a <code>JFormattedTextField</code>. <code>format</code> is
  296        * wrapped in an appropriate <code>AbstractFormatter</code> which is
  297        * then wrapped in an <code>AbstractFormatterFactory</code>.
  298        *
  299        * @param format Format used to look up an AbstractFormatter
  300        */
  301       public JFormattedTextField(java.text.Format format) {
  302           this();
  303           setFormatterFactory(getDefaultFormatterFactory(format));
  304       }
  305   
  306       /**
  307        * Creates a <code>JFormattedTextField</code> with the specified
  308        * <code>AbstractFormatter</code>. The <code>AbstractFormatter</code>
  309        * is placed in an <code>AbstractFormatterFactory</code>.
  310        *
  311        * @param formatter AbstractFormatter to use for formatting.
  312        */
  313       public JFormattedTextField(AbstractFormatter formatter) {
  314           this(new DefaultFormatterFactory(formatter));
  315       }
  316   
  317       /**
  318        * Creates a <code>JFormattedTextField</code> with the specified
  319        * <code>AbstractFormatterFactory</code>.
  320        *
  321        * @param factory AbstractFormatterFactory used for formatting.
  322        */
  323       public JFormattedTextField(AbstractFormatterFactory factory) {
  324           this();
  325           setFormatterFactory(factory);
  326       }
  327   
  328       /**
  329        * Creates a <code>JFormattedTextField</code> with the specified
  330        * <code>AbstractFormatterFactory</code> and initial value.
  331        *
  332        * @param factory <code>AbstractFormatterFactory</code> used for
  333        *        formatting.
  334        * @param currentValue Initial value to use
  335        */
  336       public JFormattedTextField(AbstractFormatterFactory factory,
  337                                  Object currentValue) {
  338           this(currentValue);
  339           setFormatterFactory(factory);
  340       }
  341   
  342       /**
  343        * Sets the behavior when focus is lost. This will be one of
  344        * <code>JFormattedTextField.COMMIT_OR_REVERT</code>,
  345        * <code>JFormattedTextField.REVERT</code>,
  346        * <code>JFormattedTextField.COMMIT</code> or
  347        * <code>JFormattedTextField.PERSIST</code>
  348        * Note that some <code>AbstractFormatter</code>s may push changes as
  349        * they occur, so that the value of this will have no effect.
  350        * <p>
  351        * This will throw an <code>IllegalArgumentException</code> if the object
  352        * passed in is not one of the afore mentioned values.
  353        * <p>
  354        * The default value of this property is
  355        * <code>JFormattedTextField.COMMIT_OR_REVERT</code>.
  356        *
  357        * @param behavior Identifies behavior when focus is lost
  358        * @throws IllegalArgumentException if behavior is not one of the known
  359        *         values
  360        * @beaninfo
  361        *  enum: COMMIT         JFormattedTextField.COMMIT
  362        *        COMMIT_OR_REVERT JFormattedTextField.COMMIT_OR_REVERT
  363        *        REVERT         JFormattedTextField.REVERT
  364        *        PERSIST        JFormattedTextField.PERSIST
  365        *  description: Behavior when component loses focus
  366        */
  367       public void setFocusLostBehavior(int behavior) {
  368           if (behavior != COMMIT && behavior != COMMIT_OR_REVERT &&
  369               behavior != PERSIST && behavior != REVERT) {
  370               throw new IllegalArgumentException("setFocusLostBehavior must be one of: JFormattedTextField.COMMIT, JFormattedTextField.COMMIT_OR_REVERT, JFormattedTextField.PERSIST or JFormattedTextField.REVERT");
  371           }
  372           focusLostBehavior = behavior;
  373       }
  374   
  375       /**
  376        * Returns the behavior when focus is lost. This will be one of
  377        * <code>COMMIT_OR_REVERT</code>,
  378        * <code>COMMIT</code>,
  379        * <code>REVERT</code> or
  380        * <code>PERSIST</code>
  381        * Note that some <code>AbstractFormatter</code>s may push changes as
  382        * they occur, so that the value of this will have no effect.
  383        *
  384        * @return returns behavior when focus is lost
  385        */
  386       public int getFocusLostBehavior() {
  387           return focusLostBehavior;
  388       }
  389   
  390       /**
  391        * Sets the <code>AbstractFormatterFactory</code>.
  392        * <code>AbstractFormatterFactory</code> is
  393        * able to return an instance of <code>AbstractFormatter</code> that is
  394        * used to format a value for display, as well an enforcing an editing
  395        * policy.
  396        * <p>
  397        * If you have not explicitly set an <code>AbstractFormatterFactory</code>
  398        * by way of this method (or a constructor) an
  399        * <code>AbstractFormatterFactory</code> and consequently an
  400        * <code>AbstractFormatter</code> will be used based on the
  401        * <code>Class</code> of the value. <code>NumberFormatter</code> will
  402        * be used for <code>Number</code>s, <code>DateFormatter</code> will
  403        * be used for <code>Dates</code>, otherwise <code>DefaultFormatter</code>
  404        * will be used.
  405        * <p>
  406        * This is a JavaBeans bound property.
  407        *
  408        * @param tf <code>AbstractFormatterFactory</code> used to lookup
  409        *          instances of <code>AbstractFormatter</code>
  410        * @beaninfo
  411        *       bound: true
  412        *   attribute: visualUpdate true
  413        * description: AbstractFormatterFactory, responsible for returning an
  414        *              AbstractFormatter that can format the current value.
  415        */
  416       public void setFormatterFactory(AbstractFormatterFactory tf) {
  417           AbstractFormatterFactory oldFactory = factory;
  418   
  419           factory = tf;
  420           firePropertyChange("formatterFactory", oldFactory, tf);
  421           setValue(getValue(), true, false);
  422       }
  423   
  424       /**
  425        * Returns the current <code>AbstractFormatterFactory</code>.
  426        *
  427        * @see #setFormatterFactory
  428        * @return <code>AbstractFormatterFactory</code> used to determine
  429        *         <code>AbstractFormatter</code>s
  430        */
  431       public AbstractFormatterFactory getFormatterFactory() {
  432           return factory;
  433       }
  434   
  435       /**
  436        * Sets the current <code>AbstractFormatter</code>.
  437        * <p>
  438        * You should not normally invoke this, instead set the
  439        * <code>AbstractFormatterFactory</code> or set the value.
  440        * <code>JFormattedTextField</code> will
  441        * invoke this as the state of the <code>JFormattedTextField</code>
  442        * changes and requires the value to be reset.
  443        * <code>JFormattedTextField</code> passes in the
  444        * <code>AbstractFormatter</code> obtained from the
  445        * <code>AbstractFormatterFactory</code>.
  446        * <p>
  447        * This is a JavaBeans bound property.
  448        *
  449        * @see #setFormatterFactory
  450        * @param format AbstractFormatter to use for formatting
  451        * @beaninfo
  452        *       bound: true
  453        *   attribute: visualUpdate true
  454        * description: TextFormatter, responsible for formatting the current value
  455        */
  456       protected void setFormatter(AbstractFormatter format) {
  457           AbstractFormatter oldFormat = this.format;
  458   
  459           if (oldFormat != null) {
  460               oldFormat.uninstall();
  461           }
  462           setEditValid(true);
  463           this.format = format;
  464           if (format != null) {
  465               format.install(this);
  466           }
  467           setEdited(false);
  468           firePropertyChange("textFormatter", oldFormat, format);
  469       }
  470   
  471       /**
  472        * Returns the <code>AbstractFormatter</code> that is used to format and
  473        * parse the current value.
  474        *
  475        * @return AbstractFormatter used for formatting
  476        */
  477       public AbstractFormatter getFormatter() {
  478           return format;
  479       }
  480   
  481       /**
  482        * Sets the value that will be formatted by an
  483        * <code>AbstractFormatter</code> obtained from the current
  484        * <code>AbstractFormatterFactory</code>. If no
  485        * <code>AbstractFormatterFactory</code> has been specified, this will
  486        * attempt to create one based on the type of <code>value</code>.
  487        * <p>
  488        * The default value of this property is null.
  489        * <p>
  490        * This is a JavaBeans bound property.
  491        *
  492        * @param value Current value to display
  493        * @beaninfo
  494        *       bound: true
  495        *   attribute: visualUpdate true
  496        * description: The value to be formatted.
  497        */
  498       public void setValue(Object value) {
  499           if (value != null && getFormatterFactory() == null) {
  500               setFormatterFactory(getDefaultFormatterFactory(value));
  501           }
  502           setValue(value, true, true);
  503       }
  504   
  505       /**
  506        * Returns the last valid value. Based on the editing policy of
  507        * the <code>AbstractFormatter</code> this may not return the current
  508        * value. The currently edited value can be obtained by invoking
  509        * <code>commitEdit</code> followed by <code>getValue</code>.
  510        *
  511        * @return Last valid value
  512        */
  513       public Object getValue() {
  514           return value;
  515       }
  516   
  517       /**
  518        * Forces the current value to be taken from the
  519        * <code>AbstractFormatter</code> and set as the current value.
  520        * This has no effect if there is no current
  521        * <code>AbstractFormatter</code> installed.
  522        *
  523        * @throws ParseException if the <code>AbstractFormatter</code> is not able
  524        *         to format the current value
  525        */
  526       public void commitEdit() throws ParseException {
  527           AbstractFormatter format = getFormatter();
  528   
  529           if (format != null) {
  530               setValue(format.stringToValue(getText()), false, true);
  531           }
  532       }
  533   
  534       /**
  535        * Sets the validity of the edit on the receiver. You should not normally
  536        * invoke this. This will be invoked by the
  537        * <code>AbstractFormatter</code> as the user edits the value.
  538        * <p>
  539        * Not all formatters will allow the component to get into an invalid
  540        * state, and thus this may never be invoked.
  541        * <p>
  542        * Based on the look and feel this may visually change the state of
  543        * the receiver.
  544        *
  545        * @param isValid boolean indicating if the currently edited value is
  546        *        valid.
  547        * @beaninfo
  548        *       bound: true
  549        *   attribute: visualUpdate true
  550        * description: True indicates the edited value is valid
  551        */
  552       private void setEditValid(boolean isValid) {
  553           if (isValid != editValid) {
  554               editValid = isValid;
  555               firePropertyChange("editValid", Boolean.valueOf(!isValid),
  556                                  Boolean.valueOf(isValid));
  557           }
  558       }
  559   
  560       /**
  561        * Returns true if the current value being edited is valid. The value of
  562        * this is managed by the current <code>AbstractFormatter</code>, as such
  563        * there is no public setter for it.
  564        *
  565        * @return true if the current value being edited is valid.
  566        */
  567       public boolean isEditValid() {
  568           return editValid;
  569       }
  570   
  571       /**
  572        * Invoked when the user inputs an invalid value. This gives the
  573        * component a chance to provide feedback. The default
  574        * implementation beeps.
  575        */
  576       protected void invalidEdit() {
  577           UIManager.getLookAndFeel().provideErrorFeedback(JFormattedTextField.this);
  578       }
  579   
  580       /**
  581        * Processes any input method events, such as
  582        * <code>InputMethodEvent.INPUT_METHOD_TEXT_CHANGED</code> or
  583        * <code>InputMethodEvent.CARET_POSITION_CHANGED</code>.
  584        *
  585        * @param e the <code>InputMethodEvent</code>
  586        * @see InputMethodEvent
  587        */
  588       protected void processInputMethodEvent(InputMethodEvent e) {
  589           AttributedCharacterIterator text = e.getText();
  590           int commitCount = e.getCommittedCharacterCount();
  591   
  592           // Keep track of the composed text
  593           if (text != null) {
  594               int begin = text.getBeginIndex();
  595               int end = text.getEndIndex();
  596               composedTextExists = ((end - begin) > commitCount);
  597           } else {
  598               composedTextExists = false;
  599           }
  600   
  601           super.processInputMethodEvent(e);
  602       }
  603   
  604       /**
  605        * Processes any focus events, such as
  606        * <code>FocusEvent.FOCUS_GAINED</code> or
  607        * <code>FocusEvent.FOCUS_LOST</code>.
  608        *
  609        * @param e the <code>FocusEvent</code>
  610        * @see FocusEvent
  611        */
  612       protected void processFocusEvent(FocusEvent e) {
  613           super.processFocusEvent(e);
  614   
  615           // ignore temporary focus event
  616           if (e.isTemporary()) {
  617               return;
  618           }
  619   
  620           if (isEdited() && e.getID() == FocusEvent.FOCUS_LOST) {
  621               InputContext ic = getInputContext();
  622               if (focusLostHandler == null) {
  623                   focusLostHandler = new FocusLostHandler();
  624               }
  625   
  626               // if there is a composed text, process it first
  627               if ((ic != null) && composedTextExists) {
  628                   ic.endComposition();
  629                   EventQueue.invokeLater(focusLostHandler);
  630               } else {
  631                   focusLostHandler.run();
  632               }
  633           }
  634           else if (!isEdited()) {
  635               // reformat
  636               setValue(getValue(), true, true);
  637           }
  638       }
  639   
  640       /**
  641        * FOCUS_LOST behavior implementation
  642        */
  643       private class FocusLostHandler implements Runnable, Serializable {
  644           public void run() {
  645               int fb = JFormattedTextField.this.getFocusLostBehavior();
  646               if (fb == JFormattedTextField.COMMIT ||
  647                   fb == JFormattedTextField.COMMIT_OR_REVERT) {
  648                   try {
  649                       JFormattedTextField.this.commitEdit();
  650                       // Give it a chance to reformat.
  651                       JFormattedTextField.this.setValue(
  652                           JFormattedTextField.this.getValue(), true, true);
  653                   } catch (ParseException pe) {
  654                       if (fb == JFormattedTextField.this.COMMIT_OR_REVERT) {
  655                           JFormattedTextField.this.setValue(
  656                               JFormattedTextField.this.getValue(), true, true);
  657                       }
  658                   }
  659               }
  660               else if (fb == JFormattedTextField.REVERT) {
  661                   JFormattedTextField.this.setValue(
  662                       JFormattedTextField.this.getValue(), true, true);
  663               }
  664           }
  665       }
  666   
  667       /**
  668        * Fetches the command list for the editor.  This is
  669        * the list of commands supported by the plugged-in UI
  670        * augmented by the collection of commands that the
  671        * editor itself supports.  These are useful for binding
  672        * to events, such as in a keymap.
  673        *
  674        * @return the command list
  675        */
  676       public Action[] getActions() {
  677           return TextAction.augmentList(super.getActions(), defaultActions);
  678       }
  679   
  680       /**
  681        * Gets the class ID for a UI.
  682        *
  683        * @return the string "FormattedTextFieldUI"
  684        * @see JComponent#getUIClassID
  685        */
  686       public String getUIClassID() {
  687           return uiClassID;
  688       }
  689   
  690       /**
  691        * Associates the editor with a text document.
  692        * The currently registered factory is used to build a view for
  693        * the document, which gets displayed by the editor after revalidation.
  694        * A PropertyChange event ("document") is propagated to each listener.
  695        *
  696        * @param doc  the document to display/edit
  697        * @see #getDocument
  698        * @beaninfo
  699        *  description: the text document model
  700        *        bound: true
  701        *       expert: true
  702        */
  703       public void setDocument(Document doc) {
  704           if (documentListener != null && getDocument() != null) {
  705               getDocument().removeDocumentListener(documentListener);
  706           }
  707           super.setDocument(doc);
  708           if (documentListener == null) {
  709               documentListener = new DocumentHandler();
  710           }
  711           doc.addDocumentListener(documentListener);
  712       }
  713   
  714       /*
  715        * See readObject and writeObject in JComponent for more
  716        * information about serialization in Swing.
  717        *
  718        * @param s Stream to write to
  719        */
  720       private void writeObject(ObjectOutputStream s) throws IOException {
  721           s.defaultWriteObject();
  722           if (getUIClassID().equals(uiClassID)) {
  723               byte count = JComponent.getWriteObjCounter(this);
  724               JComponent.setWriteObjCounter(this, --count);
  725               if (count == 0 && ui != null) {
  726                   ui.installUI(this);
  727               }
  728           }
  729       }
  730   
  731       /**
  732        * Resets the Actions that come from the TextFormatter to
  733        * <code>actions</code>.
  734        */
  735       private void setFormatterActions(Action[] actions) {
  736           if (actions == null) {
  737               if (textFormatterActionMap != null) {
  738                   textFormatterActionMap.clear();
  739               }
  740           }
  741           else {
  742               if (textFormatterActionMap == null) {
  743                   ActionMap map = getActionMap();
  744   
  745                   textFormatterActionMap = new ActionMap();
  746                   while (map != null) {
  747                       ActionMap parent = map.getParent();
  748   
  749                       if (parent instanceof UIResource || parent == null) {
  750                           map.setParent(textFormatterActionMap);
  751                           textFormatterActionMap.setParent(parent);
  752                           break;
  753                       }
  754                       map = parent;
  755                   }
  756               }
  757               for (int counter = actions.length - 1; counter >= 0;
  758                    counter--) {
  759                   Object key = actions[counter].getValue(Action.NAME);
  760   
  761                   if (key != null) {
  762                       textFormatterActionMap.put(key, actions[counter]);
  763                   }
  764               }
  765           }
  766       }
  767   
  768       /**
  769        * Does the setting of the value. If <code>createFormat</code> is true,
  770        * this will also obtain a new <code>AbstractFormatter</code> from the
  771        * current factory. The property change event will be fired if
  772        * <code>firePC</code> is true.
  773        */
  774       private void setValue(Object value, boolean createFormat, boolean firePC) {
  775           Object oldValue = this.value;
  776   
  777           this.value = value;
  778   
  779           if (createFormat) {
  780               AbstractFormatterFactory factory = getFormatterFactory();
  781               AbstractFormatter atf;
  782   
  783               if (factory != null) {
  784                   atf = factory.getFormatter(this);
  785               }
  786               else {
  787                   atf = null;
  788               }
  789               setFormatter(atf);
  790           }
  791           else {
  792               // Assumed to be valid
  793               setEditValid(true);
  794           }
  795   
  796           setEdited(false);
  797   
  798           if (firePC) {
  799               firePropertyChange("value", oldValue, value);
  800           }
  801       }
  802   
  803       /**
  804        * Sets the edited state of the receiver.
  805        */
  806       private void setEdited(boolean edited) {
  807           this.edited = edited;
  808       }
  809   
  810       /**
  811        * Returns true if the receiver has been edited.
  812        */
  813       private boolean isEdited() {
  814           return edited;
  815       }
  816   
  817       /**
  818        * Returns an AbstractFormatterFactory suitable for the passed in
  819        * Object type.
  820        */
  821       private AbstractFormatterFactory getDefaultFormatterFactory(Object type) {
  822           if (type instanceof DateFormat) {
  823               return new DefaultFormatterFactory(new DateFormatter
  824                                                  ((DateFormat)type));
  825           }
  826           if (type instanceof NumberFormat) {
  827               return new DefaultFormatterFactory(new NumberFormatter(
  828                                                  (NumberFormat)type));
  829           }
  830           if (type instanceof Format) {
  831               return new DefaultFormatterFactory(new InternationalFormatter(
  832                                                  (Format)type));
  833           }
  834           if (type instanceof Date) {
  835               return new DefaultFormatterFactory(new DateFormatter());
  836           }
  837           if (type instanceof Number) {
  838               AbstractFormatter displayFormatter = new NumberFormatter();
  839               ((NumberFormatter)displayFormatter).setValueClass(type.getClass());
  840               AbstractFormatter editFormatter = new NumberFormatter(
  841                                     new DecimalFormat("#.#"));
  842               ((NumberFormatter)editFormatter).setValueClass(type.getClass());
  843   
  844               return new DefaultFormatterFactory(displayFormatter,
  845                                                  displayFormatter,editFormatter);
  846           }
  847           return new DefaultFormatterFactory(new DefaultFormatter());
  848       }
  849   
  850   
  851       /**
  852        * Instances of <code>AbstractFormatterFactory</code> are used by
  853        * <code>JFormattedTextField</code> to obtain instances of
  854        * <code>AbstractFormatter</code> which in turn are used to format values.
  855        * <code>AbstractFormatterFactory</code> can return different
  856        * <code>AbstractFormatter</code>s based on the state of the
  857        * <code>JFormattedTextField</code>, perhaps returning different
  858        * <code>AbstractFormatter</code>s when the
  859        * <code>JFormattedTextField</code> has focus vs when it
  860        * doesn't have focus.
  861        * @since 1.4
  862        */
  863       public static abstract class AbstractFormatterFactory {
  864           /**
  865            * Returns an <code>AbstractFormatter</code> that can handle formatting
  866            * of the passed in <code>JFormattedTextField</code>.
  867            *
  868            * @param tf JFormattedTextField requesting AbstractFormatter
  869            * @return AbstractFormatter to handle formatting duties, a null
  870            *         return value implies the JFormattedTextField should behave
  871            *         like a normal JTextField
  872            */
  873           public abstract AbstractFormatter getFormatter(JFormattedTextField tf);
  874       }
  875   
  876   
  877       /**
  878        * Instances of <code>AbstractFormatter</code> are used by
  879        * <code>JFormattedTextField</code> to handle the conversion both
  880        * from an Object to a String, and back from a String to an Object.
  881        * <code>AbstractFormatter</code>s can also enfore editing policies,
  882        * or navigation policies, or manipulate the
  883        * <code>JFormattedTextField</code> in any way it sees fit to
  884        * enforce the desired policy.
  885        * <p>
  886        * An <code>AbstractFormatter</code> can only be active in
  887        * one <code>JFormattedTextField</code> at a time.
  888        * <code>JFormattedTextField</code> invokes
  889        * <code>install</code> when it is ready to use it followed
  890        * by <code>uninstall</code> when done. Subclasses
  891        * that wish to install additional state should override
  892        * <code>install</code> and message super appropriately.
  893        * <p>
  894        * Subclasses must override the conversion methods
  895        * <code>stringToValue</code> and <code>valueToString</code>. Optionally
  896        * they can override <code>getActions</code>,
  897        * <code>getNavigationFilter</code> and <code>getDocumentFilter</code>
  898        * to restrict the <code>JFormattedTextField</code> in a particular
  899        * way.
  900        * <p>
  901        * Subclasses that allow the <code>JFormattedTextField</code> to be in
  902        * a temporarily invalid state should invoke <code>setEditValid</code>
  903        * at the appropriate times.
  904        * @since 1.4
  905        */
  906       public static abstract class AbstractFormatter implements Serializable {
  907           private JFormattedTextField ftf;
  908   
  909           /**
  910            * Installs the <code>AbstractFormatter</code> onto a particular
  911            * <code>JFormattedTextField</code>.
  912            * This will invoke <code>valueToString</code> to convert the
  913            * current value from the <code>JFormattedTextField</code> to
  914            * a String. This will then install the <code>Action</code>s from
  915            * <code>getActions</code>, the <code>DocumentFilter</code>
  916            * returned from <code>getDocumentFilter</code> and the
  917            * <code>NavigationFilter</code> returned from
  918            * <code>getNavigationFilter</code> onto the
  919            * <code>JFormattedTextField</code>.
  920            * <p>
  921            * Subclasses will typically only need to override this if they
  922            * wish to install additional listeners on the
  923            * <code>JFormattedTextField</code>.
  924            * <p>
  925            * If there is a <code>ParseException</code> in converting the
  926            * current value to a String, this will set the text to an empty
  927            * String, and mark the <code>JFormattedTextField</code> as being
  928            * in an invalid state.
  929            * <p>
  930            * While this is a public method, this is typically only useful
  931            * for subclassers of <code>JFormattedTextField</code>.
  932            * <code>JFormattedTextField</code> will invoke this method at
  933            * the appropriate times when the value changes, or its internal
  934            * state changes.  You will only need to invoke this yourself if
  935            * you are subclassing <code>JFormattedTextField</code> and
  936            * installing/uninstalling <code>AbstractFormatter</code> at a
  937            * different time than <code>JFormattedTextField</code> does.
  938            *
  939            * @param ftf JFormattedTextField to format for, may be null indicating
  940            *            uninstall from current JFormattedTextField.
  941            */
  942           public void install(JFormattedTextField ftf) {
  943               if (this.ftf != null) {
  944                   uninstall();
  945               }
  946               this.ftf = ftf;
  947               if (ftf != null) {
  948                   try {
  949                       ftf.setText(valueToString(ftf.getValue()));
  950                   } catch (ParseException pe) {
  951                       ftf.setText("");
  952                       setEditValid(false);
  953                   }
  954                   installDocumentFilter(getDocumentFilter());
  955                   ftf.setNavigationFilter(getNavigationFilter());
  956                   ftf.setFormatterActions(getActions());
  957               }
  958           }
  959   
  960           /**
  961            * Uninstalls any state the <code>AbstractFormatter</code> may have
  962            * installed on the <code>JFormattedTextField</code>. This resets the
  963            * <code>DocumentFilter</code>, <code>NavigationFilter</code>
  964            * and additional <code>Action</code>s installed on the
  965            * <code>JFormattedTextField</code>.
  966            */
  967           public void uninstall() {
  968               if (this.ftf != null) {
  969                   installDocumentFilter(null);
  970                   this.ftf.setNavigationFilter(null);
  971                   this.ftf.setFormatterActions(null);
  972               }
  973           }
  974   
  975           /**
  976            * Parses <code>text</code> returning an arbitrary Object. Some
  977            * formatters may return null.
  978            *
  979            * @throws ParseException if there is an error in the conversion
  980            * @param text String to convert
  981            * @return Object representation of text
  982            */
  983           public abstract Object stringToValue(String text) throws
  984                                        ParseException;
  985   
  986           /**
  987            * Returns the string value to display for <code>value</code>.
  988            *
  989            * @throws ParseException if there is an error in the conversion
  990            * @param value Value to convert
  991            * @return String representation of value
  992            */
  993           public abstract String valueToString(Object value) throws
  994                           ParseException;
  995   
  996           /**
  997            * Returns the current <code>JFormattedTextField</code> the
  998            * <code>AbstractFormatter</code> is installed on.
  999            *
 1000            * @return JFormattedTextField formatting for.
 1001            */
 1002           protected JFormattedTextField getFormattedTextField() {
 1003               return ftf;
 1004           }
 1005   
 1006           /**
 1007            * This should be invoked when the user types an invalid character.
 1008            * This forwards the call to the current JFormattedTextField.
 1009            */
 1010           protected void invalidEdit() {
 1011               JFormattedTextField ftf = getFormattedTextField();
 1012   
 1013               if (ftf != null) {
 1014                   ftf.invalidEdit();
 1015               }
 1016           }
 1017   
 1018           /**
 1019            * Invoke this to update the <code>editValid</code> property of the
 1020            * <code>JFormattedTextField</code>. If you an enforce a policy
 1021            * such that the <code>JFormattedTextField</code> is always in a
 1022            * valid state, you will never need to invoke this.
 1023            *
 1024            * @param valid Valid state of the JFormattedTextField
 1025            */
 1026           protected void setEditValid(boolean valid) {
 1027               JFormattedTextField ftf = getFormattedTextField();
 1028   
 1029               if (ftf != null) {
 1030                   ftf.setEditValid(valid);
 1031               }
 1032           }
 1033   
 1034           /**
 1035            * Subclass and override if you wish to provide a custom set of
 1036            * <code>Action</code>s. <code>install</code> will install these
 1037            * on the <code>JFormattedTextField</code>'s <code>ActionMap</code>.
 1038            *
 1039            * @return Array of Actions to install on JFormattedTextField
 1040            */
 1041           protected Action[] getActions() {
 1042               return null;
 1043           }
 1044   
 1045           /**
 1046            * Subclass and override if you wish to provide a
 1047            * <code>DocumentFilter</code> to restrict what can be input.
 1048            * <code>install</code> will install the returned value onto
 1049            * the <code>JFormattedTextField</code>.
 1050            *
 1051            * @return DocumentFilter to restrict edits
 1052            */
 1053           protected DocumentFilter getDocumentFilter() {
 1054               return null;
 1055           }
 1056   
 1057           /**
 1058            * Subclass and override if you wish to provide a filter to restrict
 1059            * where the user can navigate to.
 1060            * <code>install</code> will install the returned value onto
 1061            * the <code>JFormattedTextField</code>.
 1062            *
 1063            * @return NavigationFilter to restrict navigation
 1064            */
 1065           protected NavigationFilter getNavigationFilter() {
 1066               return null;
 1067           }
 1068   
 1069           /**
 1070            * Clones the <code>AbstractFormatter</code>. The returned instance
 1071            * is not associated with a <code>JFormattedTextField</code>.
 1072            *
 1073            * @return Copy of the AbstractFormatter
 1074            */
 1075           protected Object clone() throws CloneNotSupportedException {
 1076               AbstractFormatter formatter = (AbstractFormatter)super.clone();
 1077   
 1078               formatter.ftf = null;
 1079               return formatter;
 1080           }
 1081   
 1082           /**
 1083            * Installs the <code>DocumentFilter</code> <code>filter</code>
 1084            * onto the current <code>JFormattedTextField</code>.
 1085            *
 1086            * @param filter DocumentFilter to install on the Document.
 1087            */
 1088           private void installDocumentFilter(DocumentFilter filter) {
 1089               JFormattedTextField ftf = getFormattedTextField();
 1090   
 1091               if (ftf != null) {
 1092                   Document doc = ftf.getDocument();
 1093   
 1094                   if (doc instanceof AbstractDocument) {
 1095                       ((AbstractDocument)doc).setDocumentFilter(filter);
 1096                   }
 1097                   doc.putProperty(DocumentFilter.class, null);
 1098               }
 1099           }
 1100       }
 1101   
 1102   
 1103       /**
 1104        * Used to commit the edit. This extends JTextField.NotifyAction
 1105        * so that <code>isEnabled</code> is true while a JFormattedTextField
 1106        * has focus, and extends <code>actionPerformed</code> to invoke
 1107        * commitEdit.
 1108        */
 1109       static class CommitAction extends JTextField.NotifyAction {
 1110           public void actionPerformed(ActionEvent e) {
 1111               JTextComponent target = getFocusedComponent();
 1112   
 1113               if (target instanceof JFormattedTextField) {
 1114                   // Attempt to commit the value
 1115                   try {
 1116                       ((JFormattedTextField)target).commitEdit();
 1117                   } catch (ParseException pe) {
 1118                       ((JFormattedTextField)target).invalidEdit();
 1119                       // value not commited, don't notify ActionListeners
 1120                       return;
 1121                   }
 1122               }
 1123               // Super behavior.
 1124               super.actionPerformed(e);
 1125           }
 1126   
 1127           public boolean isEnabled() {
 1128               JTextComponent target = getFocusedComponent();
 1129               if (target instanceof JFormattedTextField) {
 1130                   JFormattedTextField ftf = (JFormattedTextField)target;
 1131                   if (!ftf.isEdited()) {
 1132                       return false;
 1133                   }
 1134                   return true;
 1135               }
 1136               return super.isEnabled();
 1137           }
 1138       }
 1139   
 1140   
 1141       /**
 1142        * CancelAction will reset the value in the JFormattedTextField when
 1143        * <code>actionPerformed</code> is invoked. It will only be
 1144        * enabled if the focused component is an instance of
 1145        * JFormattedTextField.
 1146        */
 1147       private static class CancelAction extends TextAction {
 1148           public CancelAction() {
 1149               super("reset-field-edit");
 1150           }
 1151   
 1152           public void actionPerformed(ActionEvent e) {
 1153               JTextComponent target = getFocusedComponent();
 1154   
 1155               if (target instanceof JFormattedTextField) {
 1156                   JFormattedTextField ftf = (JFormattedTextField)target;
 1157                   ftf.setValue(ftf.getValue());
 1158               }
 1159           }
 1160   
 1161           public boolean isEnabled() {
 1162               JTextComponent target = getFocusedComponent();
 1163               if (target instanceof JFormattedTextField) {
 1164                   JFormattedTextField ftf = (JFormattedTextField)target;
 1165                   if (!ftf.isEdited()) {
 1166                       return false;
 1167                   }
 1168                   return true;
 1169               }
 1170               return super.isEnabled();
 1171           }
 1172       }
 1173   
 1174   
 1175       /**
 1176        * Sets the dirty state as the document changes.
 1177        */
 1178       private class DocumentHandler implements DocumentListener, Serializable {
 1179           public void insertUpdate(DocumentEvent e) {
 1180               setEdited(true);
 1181           }
 1182           public void removeUpdate(DocumentEvent e) {
 1183               setEdited(true);
 1184           }
 1185           public void changedUpdate(DocumentEvent e) {}
 1186       }
 1187   }

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