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