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