0001 /*
0002 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025 package javax.swing;
0026
0027 import java.awt.*;
0028 import java.awt.event.*;
0029 import java.awt.im.InputContext;
0030 import java.io.*;
0031 import java.text.*;
0032 import java.util.*;
0033 import javax.swing.UIManager;
0034 import javax.swing.event.*;
0035 import javax.swing.plaf.UIResource;
0036 import javax.swing.text.*;
0037
0038 /**
0039 * <code>JFormattedTextField</code> extends <code>JTextField</code> adding
0040 * support for formatting arbitrary values, as well as retrieving a particular
0041 * object once the user has edited the text. The following illustrates
0042 * configuring a <code>JFormattedTextField</code> to edit dates:
0043 * <pre>
0044 * JFormattedTextField ftf = new JFormattedTextField();
0045 * ftf.setValue(new Date());
0046 * </pre>
0047 * <p>
0048 * Once a <code>JFormattedTextField</code> has been created, you can
0049 * listen for editing changes by way of adding
0050 * a <code>PropertyChangeListener</code> and listening for
0051 * <code>PropertyChangeEvent</code>s with the property name <code>value</code>.
0052 * <p>
0053 * <code>JFormattedTextField</code> allows
0054 * configuring what action should be taken when focus is lost. The possible
0055 * configurations are:
0056 * <table summary="Possible JFormattedTextField configurations and their descriptions">
0057 * <tr><th><p align="left">Value</p></th><th><p align="left">Description</p></th></tr>
0058 * <tr><td>JFormattedTextField.REVERT
0059 * <td>Revert the display to match that of <code>getValue</code>,
0060 * possibly losing the current edit.
0061 * <tr><td>JFormattedTextField.COMMIT
0062 * <td>Commits the current value. If the value being edited
0063 * isn't considered a legal value by the
0064 * <code>AbstractFormatter</code> that is, a
0065 * <code>ParseException</code> is thrown, then the value
0066 * will not change, and then edited value will persist.
0067 * <tr><td>JFormattedTextField.COMMIT_OR_REVERT
0068 * <td>Similar to <code>COMMIT</code>, but if the value isn't
0069 * legal, behave like <code>REVERT</code>.
0070 * <tr><td>JFormattedTextField.PERSIST
0071 * <td>Do nothing, don't obtain a new
0072 * <code>AbstractFormatter</code>, and don't update the value.
0073 * </table>
0074 * The default is <code>JFormattedTextField.COMMIT_OR_REVERT</code>,
0075 * refer to {@link #setFocusLostBehavior} for more information on this.
0076 * <p>
0077 * <code>JFormattedTextField</code> allows the focus to leave, even if
0078 * the currently edited value is invalid. To lock the focus down while the
0079 * <code>JFormattedTextField</code> is an invalid edit state
0080 * you can attach an <code>InputVerifier</code>. The following code snippet
0081 * shows a potential implementation of such an <code>InputVerifier</code>:
0082 * <pre>
0083 * public class FormattedTextFieldVerifier extends InputVerifier {
0084 * public boolean verify(JComponent input) {
0085 * if (input instanceof JFormattedTextField) {
0086 * JFormattedTextField ftf = (JFormattedTextField)input;
0087 * AbstractFormatter formatter = ftf.getFormatter();
0088 * if (formatter != null) {
0089 * String text = ftf.getText();
0090 * try {
0091 * formatter.stringToValue(text);
0092 * return true;
0093 * } catch (ParseException pe) {
0094 * return false;
0095 * }
0096 * }
0097 * }
0098 * return true;
0099 * }
0100 * public boolean shouldYieldFocus(JComponent input) {
0101 * return verify(input);
0102 * }
0103 * }
0104 * </pre>
0105 * <p>
0106 * Alternatively, you could invoke <code>commitEdit</code>, which would also
0107 * commit the value.
0108 * <p>
0109 * <code>JFormattedTextField</code> does not do the formatting it self,
0110 * rather formatting is done through an instance of
0111 * <code>JFormattedTextField.AbstractFormatter</code> which is obtained from
0112 * an instance of <code>JFormattedTextField.AbstractFormatterFactory</code>.
0113 * Instances of <code>JFormattedTextField.AbstractFormatter</code> are
0114 * notified when they become active by way of the
0115 * <code>install</code> method, at which point the
0116 * <code>JFormattedTextField.AbstractFormatter</code> can install whatever
0117 * it needs to, typically a <code>DocumentFilter</code>. Similarly when
0118 * <code>JFormattedTextField</code> no longer
0119 * needs the <code>AbstractFormatter</code>, it will invoke
0120 * <code>uninstall</code>.
0121 * <p>
0122 * <code>JFormattedTextField</code> typically
0123 * queries the <code>AbstractFormatterFactory</code> for an
0124 * <code>AbstractFormat</code> when it gains or loses focus. Although this
0125 * can change based on the focus lost policy. If the focus lost
0126 * policy is <code>JFormattedTextField.PERSIST</code>
0127 * and the <code>JFormattedTextField</code> has been edited, the
0128 * <code>AbstractFormatterFactory</code> will not be queried until the
0129 * value has been commited. Similarly if the focus lost policy is
0130 * <code>JFormattedTextField.COMMIT</code> and an exception
0131 * is thrown from <code>stringToValue</code>, the
0132 * <code>AbstractFormatterFactory</code> will not be querired when focus is
0133 * lost or gained.
0134 * <p>
0135 * <code>JFormattedTextField.AbstractFormatter</code>
0136 * is also responsible for determining when values are commited to
0137 * the <code>JFormattedTextField</code>. Some
0138 * <code>JFormattedTextField.AbstractFormatter</code>s will make new values
0139 * available on every edit, and others will never commit the value. You can
0140 * force the current value to be obtained
0141 * from the current <code>JFormattedTextField.AbstractFormatter</code>
0142 * by way of invoking <code>commitEdit</code>. <code>commitEdit</code> will
0143 * be invoked whenever return is pressed in the
0144 * <code>JFormattedTextField</code>.
0145 * <p>
0146 * If an <code>AbstractFormatterFactory</code> has not been explicitly
0147 * set, one will be set based on the <code>Class</code> of the value type after
0148 * <code>setValue</code> has been invoked (assuming value is non-null).
0149 * For example, in the following code an appropriate
0150 * <code>AbstractFormatterFactory</code> and <code>AbstractFormatter</code>
0151 * will be created to handle formatting of numbers:
0152 * <pre>
0153 * JFormattedTextField tf = new JFormattedTextField();
0154 * tf.setValue(new Number(100));
0155 * </pre>
0156 * <p>
0157 * <strong>Warning:</strong> As the <code>AbstractFormatter</code> will
0158 * typically install a <code>DocumentFilter</code> on the
0159 * <code>Document</code>, and a <code>NavigationFilter</code> on the
0160 * <code>JFormattedTextField</code> you should not install your own. If you do,
0161 * you are likely to see odd behavior in that the editing policy of the
0162 * <code>AbstractFormatter</code> will not be enforced.
0163 * <p>
0164 * <strong>Warning:</strong> Swing is not thread safe. For more
0165 * information see <a
0166 * href="package-summary.html#threading">Swing's Threading
0167 * Policy</a>.
0168 * <p>
0169 * <strong>Warning:</strong>
0170 * Serialized objects of this class will not be compatible with
0171 * future Swing releases. The current serialization support is
0172 * appropriate for short term storage or RMI between applications running
0173 * the same version of Swing. As of 1.4, support for long term storage
0174 * of all JavaBeans<sup><font size="-2">TM</font></sup>
0175 * has been added to the <code>java.beans</code> package.
0176 * Please see {@link java.beans.XMLEncoder}.
0177 *
0178 * @version 1.31 05/05/07
0179 * @since 1.4
0180 */
0181 public class JFormattedTextField extends JTextField {
0182 private static final String uiClassID = "FormattedTextFieldUI";
0183 private static final Action[] defaultActions = {
0184 new CommitAction(), new CancelAction() };
0185
0186 /**
0187 * Constant identifying that when focus is lost,
0188 * <code>commitEdit</code> should be invoked. If in commiting the
0189 * new value a <code>ParseException</code> is thrown, the invalid
0190 * value will remain.
0191 *
0192 * @see #setFocusLostBehavior
0193 */
0194 public static final int COMMIT = 0;
0195
0196 /**
0197 * Constant identifying that when focus is lost,
0198 * <code>commitEdit</code> should be invoked. If in commiting the new
0199 * value a <code>ParseException</code> is thrown, the value will be
0200 * reverted.
0201 *
0202 * @see #setFocusLostBehavior
0203 */
0204 public static final int COMMIT_OR_REVERT = 1;
0205
0206 /**
0207 * Constant identifying that when focus is lost, editing value should
0208 * be reverted to current value set on the
0209 * <code>JFormattedTextField</code>.
0210 *
0211 * @see #setFocusLostBehavior
0212 */
0213 public static final int REVERT = 2;
0214
0215 /**
0216 * Constant identifying that when focus is lost, the edited value
0217 * should be left.
0218 *
0219 * @see #setFocusLostBehavior
0220 */
0221 public static final int PERSIST = 3;
0222
0223 /**
0224 * Factory used to obtain an instance of AbstractFormatter.
0225 */
0226 private AbstractFormatterFactory factory;
0227 /**
0228 * Object responsible for formatting the current value.
0229 */
0230 private AbstractFormatter format;
0231 /**
0232 * Last valid value.
0233 */
0234 private Object value;
0235 /**
0236 * True while the value being edited is valid.
0237 */
0238 private boolean editValid;
0239 /**
0240 * Behavior when focus is lost.
0241 */
0242 private int focusLostBehavior;
0243 /**
0244 * Indicates the current value has been edited.
0245 */
0246 private boolean edited;
0247 /**
0248 * Used to set the dirty state.
0249 */
0250 private DocumentListener documentListener;
0251 /**
0252 * Masked used to set the AbstractFormatterFactory.
0253 */
0254 private Object mask;
0255 /**
0256 * ActionMap that the TextFormatter Actions are added to.
0257 */
0258 private ActionMap textFormatterActionMap;
0259 /**
0260 * Indicates the input method composed text is in the document
0261 */
0262 private boolean composedTextExists = false;
0263 /**
0264 * A handler for FOCUS_LOST event
0265 */
0266 private FocusLostHandler focusLostHandler;
0267
0268 /**
0269 * Creates a <code>JFormattedTextField</code> with no
0270 * <code>AbstractFormatterFactory</code>. Use <code>setMask</code> or
0271 * <code>setFormatterFactory</code> to configure the
0272 * <code>JFormattedTextField</code> to edit a particular type of
0273 * value.
0274 */
0275 public JFormattedTextField() {
0276 super ();
0277 enableEvents(AWTEvent.FOCUS_EVENT_MASK);
0278 setFocusLostBehavior(COMMIT_OR_REVERT);
0279 }
0280
0281 /**
0282 * Creates a JFormattedTextField with the specified value. This will
0283 * create an <code>AbstractFormatterFactory</code> based on the
0284 * type of <code>value</code>.
0285 *
0286 * @param value Initial value for the JFormattedTextField
0287 */
0288 public JFormattedTextField(Object value) {
0289 this ();
0290 setValue(value);
0291 }
0292
0293 /**
0294 * Creates a <code>JFormattedTextField</code>. <code>format</code> is
0295 * wrapped in an appropriate <code>AbstractFormatter</code> which is
0296 * then wrapped in an <code>AbstractFormatterFactory</code>.
0297 *
0298 * @param format Format used to look up an AbstractFormatter
0299 */
0300 public JFormattedTextField(java.text.Format format) {
0301 this ();
0302 setFormatterFactory(getDefaultFormatterFactory(format));
0303 }
0304
0305 /**
0306 * Creates a <code>JFormattedTextField</code> with the specified
0307 * <code>AbstractFormatter</code>. The <code>AbstractFormatter</code>
0308 * is placed in an <code>AbstractFormatterFactory</code>.
0309 *
0310 * @param formatter AbstractFormatter to use for formatting.
0311 */
0312 public JFormattedTextField(AbstractFormatter formatter) {
0313 this (new DefaultFormatterFactory(formatter));
0314 }
0315
0316 /**
0317 * Creates a <code>JFormattedTextField</code> with the specified
0318 * <code>AbstractFormatterFactory</code>.
0319 *
0320 * @param factory AbstractFormatterFactory used for formatting.
0321 */
0322 public JFormattedTextField(AbstractFormatterFactory factory) {
0323 this ();
0324 setFormatterFactory(factory);
0325 }
0326
0327 /**
0328 * Creates a <code>JFormattedTextField</code> with the specified
0329 * <code>AbstractFormatterFactory</code> and initial value.
0330 *
0331 * @param factory <code>AbstractFormatterFactory</code> used for
0332 * formatting.
0333 * @param currentValue Initial value to use
0334 */
0335 public JFormattedTextField(AbstractFormatterFactory factory,
0336 Object currentValue) {
0337 this (currentValue);
0338 setFormatterFactory(factory);
0339 }
0340
0341 /**
0342 * Sets the behavior when focus is lost. This will be one of
0343 * <code>JFormattedTextField.COMMIT_OR_REVERT</code>,
0344 * <code>JFormattedTextField.REVERT</code>,
0345 * <code>JFormattedTextField.COMMIT</code> or
0346 * <code>JFormattedTextField.PERSIST</code>
0347 * Note that some <code>AbstractFormatter</code>s may push changes as
0348 * they occur, so that the value of this will have no effect.
0349 * <p>
0350 * This will throw an <code>IllegalArgumentException</code> if the object
0351 * passed in is not one of the afore mentioned values.
0352 * <p>
0353 * The default value of this property is
0354 * <code>JFormattedTextField.COMMIT_OR_REVERT</code>.
0355 *
0356 * @param behavior Identifies behavior when focus is lost
0357 * @throws IllegalArgumentException if behavior is not one of the known
0358 * values
0359 * @beaninfo
0360 * enum: COMMIT JFormattedTextField.COMMIT
0361 * COMMIT_OR_REVERT JFormattedTextField.COMMIT_OR_REVERT
0362 * REVERT JFormattedTextField.REVERT
0363 * PERSIST JFormattedTextField.PERSIST
0364 * description: Behavior when component loses focus
0365 */
0366 public void setFocusLostBehavior(int behavior) {
0367 if (behavior != COMMIT && behavior != COMMIT_OR_REVERT
0368 && behavior != PERSIST && behavior != REVERT) {
0369 throw new IllegalArgumentException(
0370 "setFocusLostBehavior must be one of: JFormattedTextField.COMMIT, JFormattedTextField.COMMIT_OR_REVERT, JFormattedTextField.PERSIST or JFormattedTextField.REVERT");
0371 }
0372 focusLostBehavior = behavior;
0373 }
0374
0375 /**
0376 * Returns the behavior when focus is lost. This will be one of
0377 * <code>COMMIT_OR_REVERT</code>,
0378 * <code>COMMIT</code>,
0379 * <code>REVERT</code> or
0380 * <code>PERSIST</code>
0381 * Note that some <code>AbstractFormatter</code>s may push changes as
0382 * they occur, so that the value of this will have no effect.
0383 *
0384 * @return returns behavior when focus is lost
0385 */
0386 public int getFocusLostBehavior() {
0387 return focusLostBehavior;
0388 }
0389
0390 /**
0391 * Sets the <code>AbstractFormatterFactory</code>.
0392 * <code>AbstractFormatterFactory</code> is
0393 * able to return an instance of <code>AbstractFormatter</code> that is
0394 * used to format a value for display, as well an enforcing an editing
0395 * policy.
0396 * <p>
0397 * If you have not explicitly set an <code>AbstractFormatterFactory</code>
0398 * by way of this method (or a constructor) an
0399 * <code>AbstractFormatterFactory</code> and consequently an
0400 * <code>AbstractFormatter</code> will be used based on the
0401 * <code>Class</code> of the value. <code>NumberFormatter</code> will
0402 * be used for <code>Number</code>s, <code>DateFormatter</code> will
0403 * be used for <code>Dates</code>, otherwise <code>DefaultFormatter</code>
0404 * will be used.
0405 * <p>
0406 * This is a JavaBeans bound property.
0407 *
0408 * @param tf <code>AbstractFormatterFactory</code> used to lookup
0409 * instances of <code>AbstractFormatter</code>
0410 * @beaninfo
0411 * bound: true
0412 * attribute: visualUpdate true
0413 * description: AbstractFormatterFactory, responsible for returning an
0414 * AbstractFormatter that can format the current value.
0415 */
0416 public void setFormatterFactory(AbstractFormatterFactory tf) {
0417 AbstractFormatterFactory oldFactory = factory;
0418
0419 factory = tf;
0420 firePropertyChange("formatterFactory", oldFactory, tf);
0421 setValue(getValue(), true, false);
0422 }
0423
0424 /**
0425 * Returns the current <code>AbstractFormatterFactory</code>.
0426 *
0427 * @see #setFormatterFactory
0428 * @return <code>AbstractFormatterFactory</code> used to determine
0429 * <code>AbstractFormatter</code>s
0430 */
0431 public AbstractFormatterFactory getFormatterFactory() {
0432 return factory;
0433 }
0434
0435 /**
0436 * Sets the current <code>AbstractFormatter</code>.
0437 * <p>
0438 * You should not normally invoke this, instead set the
0439 * <code>AbstractFormatterFactory</code> or set the value.
0440 * <code>JFormattedTextField</code> will
0441 * invoke this as the state of the <code>JFormattedTextField</code>
0442 * changes and requires the value to be reset.
0443 * <code>JFormattedTextField</code> passes in the
0444 * <code>AbstractFormatter</code> obtained from the
0445 * <code>AbstractFormatterFactory</code>.
0446 * <p>
0447 * This is a JavaBeans bound property.
0448 *
0449 * @see #setFormatterFactory
0450 * @param format AbstractFormatter to use for formatting
0451 * @beaninfo
0452 * bound: true
0453 * attribute: visualUpdate true
0454 * description: TextFormatter, responsible for formatting the current value
0455 */
0456 protected void setFormatter(AbstractFormatter format) {
0457 AbstractFormatter oldFormat = this .format;
0458
0459 if (oldFormat != null) {
0460 oldFormat.uninstall();
0461 }
0462 setEditValid(true);
0463 this .format = format;
0464 if (format != null) {
0465 format.install(this );
0466 }
0467 setEdited(false);
0468 firePropertyChange("textFormatter", oldFormat, format);
0469 }
0470
0471 /**
0472 * Returns the <code>AbstractFormatter</code> that is used to format and
0473 * parse the current value.
0474 *
0475 * @return AbstractFormatter used for formatting
0476 */
0477 public AbstractFormatter getFormatter() {
0478 return format;
0479 }
0480
0481 /**
0482 * Sets the value that will be formatted by an
0483 * <code>AbstractFormatter</code> obtained from the current
0484 * <code>AbstractFormatterFactory</code>. If no
0485 * <code>AbstractFormatterFactory</code> has been specified, this will
0486 * attempt to create one based on the type of <code>value</code>.
0487 * <p>
0488 * The default value of this property is null.
0489 * <p>
0490 * This is a JavaBeans bound property.
0491 *
0492 * @param value Current value to display
0493 * @beaninfo
0494 * bound: true
0495 * attribute: visualUpdate true
0496 * description: The value to be formatted.
0497 */
0498 public void setValue(Object value) {
0499 if (value != null && getFormatterFactory() == null) {
0500 setFormatterFactory(getDefaultFormatterFactory(value));
0501 }
0502 setValue(value, true, true);
0503 }
0504
0505 /**
0506 * Returns the last valid value. Based on the editing policy of
0507 * the <code>AbstractFormatter</code> this may not return the current
0508 * value. The currently edited value can be obtained by invoking
0509 * <code>commitEdit</code> followed by <code>getValue</code>.
0510 *
0511 * @return Last valid value
0512 */
0513 public Object getValue() {
0514 return value;
0515 }
0516
0517 /**
0518 * Forces the current value to be taken from the
0519 * <code>AbstractFormatter</code> and set as the current value.
0520 * This has no effect if there is no current
0521 * <code>AbstractFormatter</code> installed.
0522 *
0523 * @throws ParseException if the <code>AbstractFormatter</code> is not able
0524 * to format the current value
0525 */
0526 public void commitEdit() throws ParseException {
0527 AbstractFormatter format = getFormatter();
0528
0529 if (format != null) {
0530 setValue(format.stringToValue(getText()), false, true);
0531 }
0532 }
0533
0534 /**
0535 * Sets the validity of the edit on the receiver. You should not normally
0536 * invoke this. This will be invoked by the
0537 * <code>AbstractFormatter</code> as the user edits the value.
0538 * <p>
0539 * Not all formatters will allow the component to get into an invalid
0540 * state, and thus this may never be invoked.
0541 * <p>
0542 * Based on the look and feel this may visually change the state of
0543 * the receiver.
0544 *
0545 * @param isValid boolean indicating if the currently edited value is
0546 * valid.
0547 * @beaninfo
0548 * bound: true
0549 * attribute: visualUpdate true
0550 * description: True indicates the edited value is valid
0551 */
0552 private void setEditValid(boolean isValid) {
0553 if (isValid != editValid) {
0554 editValid = isValid;
0555 firePropertyChange("editValid", Boolean.valueOf(!isValid),
0556 Boolean.valueOf(isValid));
0557 }
0558 }
0559
0560 /**
0561 * Returns true if the current value being edited is valid. The value of
0562 * this is managed by the current <code>AbstractFormatter</code>, as such
0563 * there is no public setter for it.
0564 *
0565 * @return true if the current value being edited is valid.
0566 */
0567 public boolean isEditValid() {
0568 return editValid;
0569 }
0570
0571 /**
0572 * Invoked when the user inputs an invalid value. This gives the
0573 * component a chance to provide feedback. The default
0574 * implementation beeps.
0575 */
0576 protected void invalidEdit() {
0577 UIManager.getLookAndFeel().provideErrorFeedback(
0578 JFormattedTextField.this );
0579 }
0580
0581 /**
0582 * Processes any input method events, such as
0583 * <code>InputMethodEvent.INPUT_METHOD_TEXT_CHANGED</code> or
0584 * <code>InputMethodEvent.CARET_POSITION_CHANGED</code>.
0585 *
0586 * @param e the <code>InputMethodEvent</code>
0587 * @see InputMethodEvent
0588 */
0589 protected void processInputMethodEvent(InputMethodEvent e) {
0590 AttributedCharacterIterator text = e.getText();
0591 int commitCount = e.getCommittedCharacterCount();
0592
0593 // Keep track of the composed text
0594 if (text != null) {
0595 int begin = text.getBeginIndex();
0596 int end = text.getEndIndex();
0597 composedTextExists = ((end - begin) > commitCount);
0598 } else {
0599 composedTextExists = false;
0600 }
0601
0602 super .processInputMethodEvent(e);
0603 }
0604
0605 /**
0606 * Processes any focus events, such as
0607 * <code>FocusEvent.FOCUS_GAINED</code> or
0608 * <code>FocusEvent.FOCUS_LOST</code>.
0609 *
0610 * @param e the <code>FocusEvent</code>
0611 * @see FocusEvent
0612 */
0613 protected void processFocusEvent(FocusEvent e) {
0614 super .processFocusEvent(e);
0615
0616 // ignore temporary focus event
0617 if (e.isTemporary()) {
0618 return;
0619 }
0620
0621 if (isEdited() && e.getID() == FocusEvent.FOCUS_LOST) {
0622 InputContext ic = getInputContext();
0623 if (focusLostHandler == null) {
0624 focusLostHandler = new FocusLostHandler();
0625 }
0626
0627 // if there is a composed text, process it first
0628 if ((ic != null) && composedTextExists) {
0629 ic.endComposition();
0630 EventQueue.invokeLater(focusLostHandler);
0631 } else {
0632 focusLostHandler.run();
0633 }
0634 } else if (!isEdited()) {
0635 // reformat
0636 setValue(getValue(), true, true);
0637 }
0638 }
0639
0640 /**
0641 * FOCUS_LOST behavior implementation
0642 */
0643 private class FocusLostHandler implements Runnable, Serializable {
0644 public void run() {
0645 int fb = JFormattedTextField.this .getFocusLostBehavior();
0646 if (fb == JFormattedTextField.COMMIT
0647 || fb == JFormattedTextField.COMMIT_OR_REVERT) {
0648 try {
0649 JFormattedTextField.this .commitEdit();
0650 // Give it a chance to reformat.
0651 JFormattedTextField.this .setValue(
0652 JFormattedTextField.this .getValue(), true,
0653 true);
0654 } catch (ParseException pe) {
0655 if (fb == JFormattedTextField.this .COMMIT_OR_REVERT) {
0656 JFormattedTextField.this .setValue(
0657 JFormattedTextField.this .getValue(),
0658 true, true);
0659 }
0660 }
0661 } else if (fb == JFormattedTextField.REVERT) {
0662 JFormattedTextField.this
0663 .setValue(JFormattedTextField.this .getValue(),
0664 true, true);
0665 }
0666 }
0667 }
0668
0669 /**
0670 * Fetches the command list for the editor. This is
0671 * the list of commands supported by the plugged-in UI
0672 * augmented by the collection of commands that the
0673 * editor itself supports. These are useful for binding
0674 * to events, such as in a keymap.
0675 *
0676 * @return the command list
0677 */
0678 public Action[] getActions() {
0679 return TextAction.augmentList(super .getActions(),
0680 defaultActions);
0681 }
0682
0683 /**
0684 * Gets the class ID for a UI.
0685 *
0686 * @return the string "FormattedTextFieldUI"
0687 * @see JComponent#getUIClassID
0688 */
0689 public String getUIClassID() {
0690 return uiClassID;
0691 }
0692
0693 /**
0694 * Associates the editor with a text document.
0695 * The currently registered factory is used to build a view for
0696 * the document, which gets displayed by the editor after revalidation.
0697 * A PropertyChange event ("document") is propagated to each listener.
0698 *
0699 * @param doc the document to display/edit
0700 * @see #getDocument
0701 * @beaninfo
0702 * description: the text document model
0703 * bound: true
0704 * expert: true
0705 */
0706 public void setDocument(Document doc) {
0707 if (documentListener != null && getDocument() != null) {
0708 getDocument().removeDocumentListener(documentListener);
0709 }
0710 super .setDocument(doc);
0711 if (documentListener == null) {
0712 documentListener = new DocumentHandler();
0713 }
0714 doc.addDocumentListener(documentListener);
0715 }
0716
0717 /*
0718 * See readObject and writeObject in JComponent for more
0719 * information about serialization in Swing.
0720 *
0721 * @param s Stream to write to
0722 */
0723 private void writeObject(ObjectOutputStream s) throws IOException {
0724 s.defaultWriteObject();
0725 if (getUIClassID().equals(uiClassID)) {
0726 byte count = JComponent.getWriteObjCounter(this );
0727 JComponent.setWriteObjCounter(this , --count);
0728 if (count == 0 && ui != null) {
0729 ui.installUI(this );
0730 }
0731 }
0732 }
0733
0734 /**
0735 * Resets the Actions that come from the TextFormatter to
0736 * <code>actions</code>.
0737 */
0738 private void setFormatterActions(Action[] actions) {
0739 if (actions == null) {
0740 if (textFormatterActionMap != null) {
0741 textFormatterActionMap.clear();
0742 }
0743 } else {
0744 if (textFormatterActionMap == null) {
0745 ActionMap map = getActionMap();
0746
0747 textFormatterActionMap = new ActionMap();
0748 while (map != null) {
0749 ActionMap parent = map.getParent();
0750
0751 if (parent instanceof UIResource || parent == null) {
0752 map.setParent(textFormatterActionMap);
0753 textFormatterActionMap.setParent(parent);
0754 break;
0755 }
0756 map = parent;
0757 }
0758 }
0759 for (int counter = actions.length - 1; counter >= 0; counter--) {
0760 Object key = actions[counter].getValue(Action.NAME);
0761
0762 if (key != null) {
0763 textFormatterActionMap.put(key, actions[counter]);
0764 }
0765 }
0766 }
0767 }
0768
0769 /**
0770 * Does the setting of the value. If <code>createFormat</code> is true,
0771 * this will also obtain a new <code>AbstractFormatter</code> from the
0772 * current factory. The property change event will be fired if
0773 * <code>firePC</code> is true.
0774 */
0775 private void setValue(Object value, boolean createFormat,
0776 boolean firePC) {
0777 Object oldValue = this .value;
0778
0779 this .value = value;
0780
0781 if (createFormat) {
0782 AbstractFormatterFactory factory = getFormatterFactory();
0783 AbstractFormatter atf;
0784
0785 if (factory != null) {
0786 atf = factory.getFormatter(this );
0787 } else {
0788 atf = null;
0789 }
0790 setFormatter(atf);
0791 } else {
0792 // Assumed to be valid
0793 setEditValid(true);
0794 }
0795
0796 setEdited(false);
0797
0798 if (firePC) {
0799 firePropertyChange("value", oldValue, value);
0800 }
0801 }
0802
0803 /**
0804 * Sets the edited state of the receiver.
0805 */
0806 private void setEdited(boolean edited) {
0807 this .edited = edited;
0808 }
0809
0810 /**
0811 * Returns true if the receiver has been edited.
0812 */
0813 private boolean isEdited() {
0814 return edited;
0815 }
0816
0817 /**
0818 * Returns an AbstractFormatterFactory suitable for the passed in
0819 * Object type.
0820 */
0821 private AbstractFormatterFactory getDefaultFormatterFactory(
0822 Object type) {
0823 if (type instanceof DateFormat) {
0824 return new DefaultFormatterFactory(new DateFormatter(
0825 (DateFormat) type));
0826 }
0827 if (type instanceof NumberFormat) {
0828 return new DefaultFormatterFactory(new NumberFormatter(
0829 (NumberFormat) type));
0830 }
0831 if (type instanceof Format) {
0832 return new DefaultFormatterFactory(
0833 new InternationalFormatter((Format) type));
0834 }
0835 if (type instanceof Date) {
0836 return new DefaultFormatterFactory(new DateFormatter());
0837 }
0838 if (type instanceof Number) {
0839 AbstractFormatter displayFormatter = new NumberFormatter();
0840 ((NumberFormatter) displayFormatter).setValueClass(type
0841 .getClass());
0842 AbstractFormatter editFormatter = new NumberFormatter(
0843 new DecimalFormat("#.#"));
0844 ((NumberFormatter) editFormatter).setValueClass(type
0845 .getClass());
0846
0847 return new DefaultFormatterFactory(displayFormatter,
0848 displayFormatter, editFormatter);
0849 }
0850 return new DefaultFormatterFactory(new DefaultFormatter());
0851 }
0852
0853 /**
0854 * Instances of <code>AbstractFormatterFactory</code> are used by
0855 * <code>JFormattedTextField</code> to obtain instances of
0856 * <code>AbstractFormatter</code> which in turn are used to format values.
0857 * <code>AbstractFormatterFactory</code> can return different
0858 * <code>AbstractFormatter</code>s based on the state of the
0859 * <code>JFormattedTextField</code>, perhaps returning different
0860 * <code>AbstractFormatter</code>s when the
0861 * <code>JFormattedTextField</code> has focus vs when it
0862 * doesn't have focus.
0863 * @since 1.4
0864 */
0865 public static abstract class AbstractFormatterFactory {
0866 /**
0867 * Returns an <code>AbstractFormatter</code> that can handle formatting
0868 * of the passed in <code>JFormattedTextField</code>.
0869 *
0870 * @param tf JFormattedTextField requesting AbstractFormatter
0871 * @return AbstractFormatter to handle formatting duties, a null
0872 * return value implies the JFormattedTextField should behave
0873 * like a normal JTextField
0874 */
0875 public abstract AbstractFormatter getFormatter(
0876 JFormattedTextField tf);
0877 }
0878
0879 /**
0880 * Instances of <code>AbstractFormatter</code> are used by
0881 * <code>JFormattedTextField</code> to handle the conversion both
0882 * from an Object to a String, and back from a String to an Object.
0883 * <code>AbstractFormatter</code>s can also enfore editing policies,
0884 * or navigation policies, or manipulate the
0885 * <code>JFormattedTextField</code> in any way it sees fit to
0886 * enforce the desired policy.
0887 * <p>
0888 * An <code>AbstractFormatter</code> can only be active in
0889 * one <code>JFormattedTextField</code> at a time.
0890 * <code>JFormattedTextField</code> invokes
0891 * <code>install</code> when it is ready to use it followed
0892 * by <code>uninstall</code> when done. Subclasses
0893 * that wish to install additional state should override
0894 * <code>install</code> and message super appropriately.
0895 * <p>
0896 * Subclasses must override the conversion methods
0897 * <code>stringToValue</code> and <code>valueToString</code>. Optionally
0898 * they can override <code>getActions</code>,
0899 * <code>getNavigationFilter</code> and <code>getDocumentFilter</code>
0900 * to restrict the <code>JFormattedTextField</code> in a particular
0901 * way.
0902 * <p>
0903 * Subclasses that allow the <code>JFormattedTextField</code> to be in
0904 * a temporarily invalid state should invoke <code>setEditValid</code>
0905 * at the appropriate times.
0906 * @since 1.4
0907 */
0908 public static abstract class AbstractFormatter implements
0909 Serializable {
0910 private JFormattedTextField ftf;
0911
0912 /**
0913 * Installs the <code>AbstractFormatter</code> onto a particular
0914 * <code>JFormattedTextField</code>.
0915 * This will invoke <code>valueToString</code> to convert the
0916 * current value from the <code>JFormattedTextField</code> to
0917 * a String. This will then install the <code>Action</code>s from
0918 * <code>getActions</code>, the <code>DocumentFilter</code>
0919 * returned from <code>getDocumentFilter</code> and the
0920 * <code>NavigationFilter</code> returned from
0921 * <code>getNavigationFilter</code> onto the
0922 * <code>JFormattedTextField</code>.
0923 * <p>
0924 * Subclasses will typically only need to override this if they
0925 * wish to install additional listeners on the
0926 * <code>JFormattedTextField</code>.
0927 * <p>
0928 * If there is a <code>ParseException</code> in converting the
0929 * current value to a String, this will set the text to an empty
0930 * String, and mark the <code>JFormattedTextField</code> as being
0931 * in an invalid state.
0932 * <p>
0933 * While this is a public method, this is typically only useful
0934 * for subclassers of <code>JFormattedTextField</code>.
0935 * <code>JFormattedTextField</code> will invoke this method at
0936 * the appropriate times when the value changes, or its internal
0937 * state changes. You will only need to invoke this yourself if
0938 * you are subclassing <code>JFormattedTextField</code> and
0939 * installing/uninstalling <code>AbstractFormatter</code> at a
0940 * different time than <code>JFormattedTextField</code> does.
0941 *
0942 * @param ftf JFormattedTextField to format for, may be null indicating
0943 * uninstall from current JFormattedTextField.
0944 */
0945 public void install(JFormattedTextField ftf) {
0946 if (this .ftf != null) {
0947 uninstall();
0948 }
0949 this .ftf = ftf;
0950 if (ftf != null) {
0951 try {
0952 ftf.setText(valueToString(ftf.getValue()));
0953 } catch (ParseException pe) {
0954 ftf.setText("");
0955 setEditValid(false);
0956 }
0957 installDocumentFilter(getDocumentFilter());
0958 ftf.setNavigationFilter(getNavigationFilter());
0959 ftf.setFormatterActions(getActions());
0960 }
0961 }
0962
0963 /**
0964 * Uninstalls any state the <code>AbstractFormatter</code> may have
0965 * installed on the <code>JFormattedTextField</code>. This resets the
0966 * <code>DocumentFilter</code>, <code>NavigationFilter</code>
0967 * and additional <code>Action</code>s installed on the
0968 * <code>JFormattedTextField</code>.
0969 */
0970 public void uninstall() {
0971 if (this .ftf != null) {
0972 installDocumentFilter(null);
0973 this .ftf.setNavigationFilter(null);
0974 this .ftf.setFormatterActions(null);
0975 }
0976 }
0977
0978 /**
0979 * Parses <code>text</code> returning an arbitrary Object. Some
0980 * formatters may return null.
0981 *
0982 * @throws ParseException if there is an error in the conversion
0983 * @param text String to convert
0984 * @return Object representation of text
0985 */
0986 public abstract Object stringToValue(String text)
0987 throws ParseException;
0988
0989 /**
0990 * Returns the string value to display for <code>value</code>.
0991 *
0992 * @throws ParseException if there is an error in the conversion
0993 * @param value Value to convert
0994 * @return String representation of value
0995 */
0996 public abstract String valueToString(Object value)
0997 throws ParseException;
0998
0999 /**
1000 * Returns the current <code>JFormattedTextField</code> the
1001 * <code>AbstractFormatter</code> is installed on.
1002 *
1003 * @return JFormattedTextField formatting for.
1004 */
1005 protected JFormattedTextField getFormattedTextField() {
1006 return ftf;
1007 }
1008
1009 /**
1010 * This should be invoked when the user types an invalid character.
1011 * This forwards the call to the current JFormattedTextField.
1012 */
1013 protected void invalidEdit() {
1014 JFormattedTextField ftf = getFormattedTextField();
1015
1016 if (ftf != null) {
1017 ftf.invalidEdit();
1018 }
1019 }
1020
1021 /**
1022 * Invoke this to update the <code>editValid</code> property of the
1023 * <code>JFormattedTextField</code>. If you an enforce a policy
1024 * such that the <code>JFormattedTextField</code> is always in a
1025 * valid state, you will never need to invoke this.
1026 *
1027 * @param valid Valid state of the JFormattedTextField
1028 */
1029 protected void setEditValid(boolean valid) {
1030 JFormattedTextField ftf = getFormattedTextField();
1031
1032 if (ftf != null) {
1033 ftf.setEditValid(valid);
1034 }
1035 }
1036
1037 /**
1038 * Subclass and override if you wish to provide a custom set of
1039 * <code>Action</code>s. <code>install</code> will install these
1040 * on the <code>JFormattedTextField</code>'s <code>ActionMap</code>.
1041 *
1042 * @return Array of Actions to install on JFormattedTextField
1043 */
1044 protected Action[] getActions() {
1045 return null;
1046 }
1047
1048 /**
1049 * Subclass and override if you wish to provide a
1050 * <code>DocumentFilter</code> to restrict what can be input.
1051 * <code>install</code> will install the returned value onto
1052 * the <code>JFormattedTextField</code>.
1053 *
1054 * @return DocumentFilter to restrict edits
1055 */
1056 protected DocumentFilter getDocumentFilter() {
1057 return null;
1058 }
1059
1060 /**
1061 * Subclass and override if you wish to provide a filter to restrict
1062 * where the user can navigate to.
1063 * <code>install</code> will install the returned value onto
1064 * the <code>JFormattedTextField</code>.
1065 *
1066 * @return NavigationFilter to restrict navigation
1067 */
1068 protected NavigationFilter getNavigationFilter() {
1069 return null;
1070 }
1071
1072 /**
1073 * Clones the <code>AbstractFormatter</code>. The returned instance
1074 * is not associated with a <code>JFormattedTextField</code>.
1075 *
1076 * @return Copy of the AbstractFormatter
1077 */
1078 protected Object clone() throws CloneNotSupportedException {
1079 AbstractFormatter formatter = (AbstractFormatter) super
1080 .clone();
1081
1082 formatter.ftf = null;
1083 return formatter;
1084 }
1085
1086 /**
1087 * Installs the <code>DocumentFilter</code> <code>filter</code>
1088 * onto the current <code>JFormattedTextField</code>.
1089 *
1090 * @param filter DocumentFilter to install on the Document.
1091 */
1092 private void installDocumentFilter(DocumentFilter filter) {
1093 JFormattedTextField ftf = getFormattedTextField();
1094
1095 if (ftf != null) {
1096 Document doc = ftf.getDocument();
1097
1098 if (doc instanceof AbstractDocument) {
1099 ((AbstractDocument) doc).setDocumentFilter(filter);
1100 }
1101 doc.putProperty(DocumentFilter.class, null);
1102 }
1103 }
1104 }
1105
1106 /**
1107 * Used to commit the edit. This extends JTextField.NotifyAction
1108 * so that <code>isEnabled</code> is true while a JFormattedTextField
1109 * has focus, and extends <code>actionPerformed</code> to invoke
1110 * commitEdit.
1111 */
1112 static class CommitAction extends JTextField.NotifyAction {
1113 public void actionPerformed(ActionEvent e) {
1114 JTextComponent target = getFocusedComponent();
1115
1116 if (target instanceof JFormattedTextField) {
1117 // Attempt to commit the value
1118 try {
1119 ((JFormattedTextField) target).commitEdit();
1120 } catch (ParseException pe) {
1121 ((JFormattedTextField) target).invalidEdit();
1122 // value not commited, don't notify ActionListeners
1123 return;
1124 }
1125 }
1126 // Super behavior.
1127 super .actionPerformed(e);
1128 }
1129
1130 public boolean isEnabled() {
1131 JTextComponent target = getFocusedComponent();
1132 if (target instanceof JFormattedTextField) {
1133 JFormattedTextField ftf = (JFormattedTextField) target;
1134 if (!ftf.isEdited()) {
1135 return false;
1136 }
1137 return true;
1138 }
1139 return super .isEnabled();
1140 }
1141 }
1142
1143 /**
1144 * CancelAction will reset the value in the JFormattedTextField when
1145 * <code>actionPerformed</code> is invoked. It will only be
1146 * enabled if the focused component is an instance of
1147 * JFormattedTextField.
1148 */
1149 private static class CancelAction extends TextAction {
1150 public CancelAction() {
1151 super ("reset-field-edit");
1152 }
1153
1154 public void actionPerformed(ActionEvent e) {
1155 JTextComponent target = getFocusedComponent();
1156
1157 if (target instanceof JFormattedTextField) {
1158 JFormattedTextField ftf = (JFormattedTextField) target;
1159 ftf.setValue(ftf.getValue());
1160 }
1161 }
1162
1163 public boolean isEnabled() {
1164 JTextComponent target = getFocusedComponent();
1165 if (target instanceof JFormattedTextField) {
1166 JFormattedTextField ftf = (JFormattedTextField) target;
1167 if (!ftf.isEdited()) {
1168 return false;
1169 }
1170 return true;
1171 }
1172 return super .isEnabled();
1173 }
1174 }
1175
1176 /**
1177 * Sets the dirty state as the document changes.
1178 */
1179 private class DocumentHandler implements DocumentListener,
1180 Serializable {
1181 public void insertUpdate(DocumentEvent e) {
1182 setEdited(true);
1183 }
1184
1185 public void removeUpdate(DocumentEvent e) {
1186 setEdited(true);
1187 }
1188
1189 public void changedUpdate(DocumentEvent e) {
1190 }
1191 }
1192 }
|