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