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