Merge from mainline
[official-gcc.git] / libjava / classpath / javax / swing / JFormattedTextField.java
blob761955d6dd91d81b0e4af959e1c7187163eb7d91
1 /* JFormattedTextField.java --
2 Copyright (C) 2003, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing;
41 import java.awt.event.FocusEvent;
42 import java.io.Serializable;
43 import java.text.DateFormat;
44 import java.text.Format;
45 import java.text.NumberFormat;
46 import java.text.ParseException;
47 import java.util.Date;
49 import javax.swing.text.AbstractDocument;
50 import javax.swing.text.DateFormatter;
51 import javax.swing.text.DefaultFormatter;
52 import javax.swing.text.DefaultFormatterFactory;
53 import javax.swing.text.Document;
54 import javax.swing.text.DocumentFilter;
55 import javax.swing.text.InternationalFormatter;
56 import javax.swing.text.NavigationFilter;
57 import javax.swing.text.NumberFormatter;
59 /**
60 * A text field that makes use of a formatter to display and edit a specific
61 * type of data. The value that is displayed can be an arbitrary object. The
62 * formatter is responsible for displaying the value in a textual form and
63 * it may allow editing of the value.
65 * Formatters are usually obtained using an instance of
66 * {@link AbstractFormatterFactory}. This factory is responsible for providing
67 * an instance of {@link AbstractFormatter} that is able to handle the
68 * formatting of the value of the JFormattedTextField.
70 * @author Michael Koch
71 * @author Anthony Balkissoon abalkiss at redhat dot com
73 * @since 1.4
75 public class JFormattedTextField extends JTextField
77 private static final long serialVersionUID = 5464657870110180632L;
79 /**
80 * An abstract base implementation for a formatter that can be used by
81 * a JTextField. A formatter can display a specific type of object and
82 * may provide a way to edit this value.
84 public abstract static class AbstractFormatter implements Serializable
86 private static final long serialVersionUID = -5193212041738979680L;
88 private JFormattedTextField textField;
90 public AbstractFormatter ()
92 //Do nothing here.
95 /**
96 * Clones the AbstractFormatter and removes the association to any
97 * particular JFormattedTextField.
99 * @return a clone of this formatter with no association to any particular
100 * JFormattedTextField
101 * @throws CloneNotSupportedException if the Object's class doesn't support
102 * the {@link Cloneable} interface
104 protected Object clone ()
105 throws CloneNotSupportedException
107 // Clone this formatter.
108 AbstractFormatter newFormatter = (AbstractFormatter)super.clone();
110 // And remove the association to the JFormattedTextField.
111 newFormatter.textField = null;
112 return newFormatter;
116 * Returns a custom set of Actions that this formatter supports. Should
117 * be subclassed by formatters that have a custom set of Actions.
119 * @return <code>null</code>. Should be subclassed by formatters that want
120 * to install custom Actions on the JFormattedTextField.
122 protected Action[] getActions ()
124 return null;
128 * Gets the DocumentFilter for this formatter. Should be subclassed
129 * by formatters wishing to install a filter that oversees Document
130 * mutations.
132 * @return <code>null</code>. Should be subclassed by formatters
133 * that want to restrict Document mutations.
135 protected DocumentFilter getDocumentFilter ()
137 // Subclasses should override this if they want to install a
138 // DocumentFilter.
139 return null;
143 * Returns the JFormattedTextField on which this formatter is
144 * currently installed.
146 * @return the JFormattedTextField on which this formatter is currently
147 * installed
149 protected JFormattedTextField getFormattedTextField ()
151 return textField;
155 * Gets the NavigationFilter for this formatter. Should be subclassed
156 * by formatters (such as {@link DefaultFormatter}) that wish to
157 * restrict where the cursor can be placed within the text field.
159 * @return <code>null</code>. Subclassed by formatters that want to restrict
160 * cursor location within the JFormattedTextField.
162 protected NavigationFilter getNavigationFilter ()
164 // This should be subclassed if the formatter wants to install
165 // a NavigationFilter on the JFormattedTextField.
166 return null;
170 * Installs this formatter on the specified JFormattedTextField. This
171 * converts the current value to a displayable String and displays it,
172 * and installs formatter specific Actions from <code>getActions</code>.
173 * It also installs a DocumentFilter and NavigationFilter on the
174 * JFormattedTextField.
175 * <p>
176 * If there is a <code>ParseException</code> this sets the text to an
177 * empty String and marks the text field in an invalid state.
179 * @param textField the JFormattedTextField on which to install this
180 * formatter
182 public void install(JFormattedTextField textField)
184 // Uninstall the current textfield.
185 if (this.textField != null)
186 uninstall();
188 this.textField = textField;
190 // Install some state on the text field, including display text,
191 // DocumentFilter, NavigationFilter, and formatter specific Actions.
192 if (textField != null)
196 // Set the text of the field.
197 textField.setText(valueToString(textField.getValue()));
198 Document doc = textField.getDocument();
200 // Set the DocumentFilter for the field's Document.
201 if (doc instanceof AbstractDocument)
202 ((AbstractDocument)doc).setDocumentFilter(getDocumentFilter());
204 // Set the NavigationFilter.
205 textField.setNavigationFilter(getNavigationFilter());
207 // Set the Formatter Actions
208 // FIXME: Have to add the actions from getActions()
210 catch (ParseException pe)
212 // Set the text to an empty String and mark the field as invalid.
213 textField.setText("");
214 setEditValid(false);
220 * Clears the state installed on the JFormattedTextField by the formatter.
221 * This resets the DocumentFilter, NavigationFilter, and any additional
222 * Actions (returned by <code>getActions()</code>).
224 public void uninstall ()
226 // Set the DocumentFilter for the field's Document.
227 Document doc = textField.getDocument();
228 if (doc instanceof AbstractDocument)
229 ((AbstractDocument)doc).setDocumentFilter(null);
230 textField.setNavigationFilter(null);
231 // FIXME: Have to remove the Actions from getActions()
232 this.textField = null;
236 * Invoke this method when invalid values are entered. This forwards the
237 * call to the JFormattedTextField.
239 protected void invalidEdit ()
241 textField.invalidEdit();
245 * This method updates the <code>editValid</code> property of
246 * JFormattedTextField.
248 * @param valid the new state for the <code>editValid</code> property
250 protected void setEditValid (boolean valid)
252 textField.editValid = valid;
256 * Parses <code>text</code> to return a corresponding Object.
258 * @param text the String to parse
259 * @return an Object that <code>text</code> represented
260 * @throws ParseException if there is an error in the conversion
262 public abstract Object stringToValue (String text)
263 throws ParseException;
266 * Returns a String to be displayed, based on the Object
267 * <code>value</code>.
269 * @param value the Object from which to generate a String
270 * @return a String to be displayed
271 * @throws ParseException if there is an error in the conversion
273 public abstract String valueToString (Object value)
274 throws ParseException;
278 * Delivers instances of an {@link AbstractFormatter} for
279 * a specific value type for a JFormattedTextField.
281 public abstract static class AbstractFormatterFactory
283 public AbstractFormatterFactory ()
285 // Do nothing here.
288 public abstract AbstractFormatter getFormatter (JFormattedTextField tf);
291 /** The possible focusLostBehavior options **/
292 public static final int COMMIT = 0;
293 public static final int COMMIT_OR_REVERT = 1;
294 public static final int REVERT = 2;
295 public static final int PERSIST = 3;
297 /** The most recent valid and committed value **/
298 private Object value;
300 /** The behaviour for when this text field loses focus **/
301 private int focusLostBehavior = COMMIT_OR_REVERT;
303 /** The formatter factory currently being used **/
304 private AbstractFormatterFactory formatterFactory;
306 /** The formatter currently being used **/
307 private AbstractFormatter formatter;
309 // Package-private to avoid an accessor method.
310 boolean editValid = true;
313 * Creates a JFormattedTextField with no formatter factory.
314 * <code>setValue</code> or <code>setFormatterFactory</code> will
315 * properly configure this text field to edit a particular type
316 * of value.
318 public JFormattedTextField ()
320 this((AbstractFormatterFactory) null, null);
324 * Creates a JFormattedTextField that can handle the specified Format.
325 * An appopriate AbstractFormatter and AbstractFormatterFactory will
326 * be created for the specified Format.
328 * @param format the Format that this JFormattedTextField should be able
329 * to handle
331 public JFormattedTextField (Format format)
333 this ();
334 setFormatterFactory(getAppropriateFormatterFactory(format));
338 * Creates a JFormattedTextField with the specified formatter. This will
339 * create a {@link DefaultFormatterFactory} with this formatter as the default
340 * formatter.
342 * @param formatter the formatter to use for this JFormattedTextField
344 public JFormattedTextField (AbstractFormatter formatter)
346 this(new DefaultFormatterFactory (formatter));
350 * Creates a JFormattedTextField with the specified formatter factory.
352 * @param factory the formatter factory to use for this JFormattedTextField
354 public JFormattedTextField (AbstractFormatterFactory factory)
356 setFormatterFactory(factory);
360 * Creates a JFormattedTextField with the specified formatter factory and
361 * initial value.
363 * @param factory the initial formatter factory for this JFormattedTextField
364 * @param value the initial value for the text field
366 public JFormattedTextField (AbstractFormatterFactory factory, Object value)
368 setFormatterFactory(factory);
369 setValue(value);
373 * Creates a JFormattedTextField with the specified value. This creates a
374 * formatter and formatterFactory that are appropriate for the value.
376 * @param value the initial value for this JFormattedTextField
378 public JFormattedTextField (Object value)
380 setValue(value);
384 * Returns an AbstractFormatterFactory that will give an appropriate
385 * AbstractFormatter for the given Format.
386 * @param format the Format to match with an AbstractFormatter.
387 * @return a DefaultFormatterFactory whose defaultFormatter is appropriate
388 * for the given Format.
390 private AbstractFormatterFactory getAppropriateFormatterFactory (Format format)
392 AbstractFormatter newFormatter;
393 if (format instanceof DateFormat)
394 newFormatter = new DateFormatter((DateFormat)format);
395 else if (format instanceof NumberFormat)
396 newFormatter = new NumberFormatter ((NumberFormat)format);
397 else
398 newFormatter = new InternationalFormatter(format);
400 return new DefaultFormatterFactory(newFormatter);
404 * Forces the current value from the editor to be set as the current
405 * value. If there is no current formatted this has no effect.
407 * @throws ParseException if the formatter cannot format the current value
409 public void commitEdit ()
410 throws ParseException
412 if (formatter == null)
413 return;
414 // Note: this code is a lot like setValue except that we don't want
415 // to create a new formatter.
416 Object oldValue = this.value;
418 this.value = formatter.stringToValue(getText());;
419 editValid = true;
421 firePropertyChange("value", oldValue, this.value);
425 * Gets the command list supplied by the UI augmented by the specific
426 * Actions for JFormattedTextField.
428 * @return an array of Actions that this text field supports
430 public Action[] getActions ()
432 // FIXME: Add JFormattedTextField specific actions
433 // These are related to committing or cancelling edits.
434 return super.getActions();
438 * Returns the behaviour of this JFormattedTextField upon losing focus. This
439 * is one of <code>COMMIT</code>, <code>COMMIT_OR_REVERT</code>,
440 * <code>PERSIST</code>, or <code>REVERT</code>.
441 * @return the behaviour upon losing focus
443 public int getFocusLostBehavior()
445 return focusLostBehavior;
449 * Returns the current formatter used for this JFormattedTextField.
450 * @return the current formatter used for this JFormattedTextField
452 public AbstractFormatter getFormatter ()
454 return formatter;
458 * Returns the factory currently used to generate formatters for this
459 * JFormattedTextField.
460 * @return the factory currently used to generate formatters
462 public AbstractFormatterFactory getFormatterFactory ()
464 return formatterFactory;
467 public String getUIClassID ()
469 return "FormattedTextFieldUI";
473 * Returns the last valid value. This may not be the value currently shown
474 * in the text field depending on whether or not the formatter commits on
475 * valid edits and allows invalid input to be temporarily displayed.
476 * @return the last committed valid value
478 public Object getValue ()
480 return value;
484 * This method is used to provide feedback to the user when an invalid value
485 * is input during editing.
487 protected void invalidEdit ()
489 UIManager.getLookAndFeel().provideErrorFeedback(this);
493 * Returns true if the current value being edited is valid. This property is
494 * managed by the current formatted.
495 * @return true if the value being edited is valid.
497 public boolean isEditValid ()
499 return editValid;
503 * Processes focus events. This is overridden because we may want to
504 * change the formatted depending on whether or not this field has
505 * focus.
507 * @param evt the FocusEvent
509 protected void processFocusEvent (FocusEvent evt)
511 super.processFocusEvent(evt);
512 // Let the formatterFactory change the formatter for this text field
513 // based on whether or not it has focus.
514 setFormatter (formatterFactory.getFormatter(this));
518 * Associates this JFormattedTextField with a Document and propagates
519 * a PropertyChange event to each listener.
521 * @param newDocument the Document to associate with this text field
523 public void setDocument(Document newDocument)
525 // FIXME: This method should do more than this. Must do some handling
526 // of the DocumentListeners.
527 Document oldDocument = getDocument();
529 if (oldDocument == newDocument)
530 return;
532 super.setDocument(newDocument);
536 * Sets the behaviour of this JFormattedTextField upon losing focus.
537 * This must be <code>COMMIT</code>, <code>COMMIT_OR_REVERT</code>,
538 * <code>PERSIST</code>, or <code>REVERT</code> or an
539 * IllegalArgumentException will be thrown.
541 * @param behavior
542 * @throws IllegalArgumentException if <code>behaviour</code> is not
543 * one of the above
545 public void setFocusLostBehavior(int behavior)
547 if (behavior != COMMIT
548 && behavior != COMMIT_OR_REVERT
549 && behavior != PERSIST
550 && behavior != REVERT)
551 throw new IllegalArgumentException("invalid behavior");
553 this.focusLostBehavior = behavior;
557 * Sets the formatter for this JFormattedTextField. Normally the formatter
558 * factory will take care of this, or calls to setValue will also make sure
559 * that the formatter is set appropriately.
561 * @param formatter the AbstractFormatter to use for formatting the value for
562 * this JFormattedTextField
564 protected void setFormatter (AbstractFormatter formatter)
566 AbstractFormatter oldFormatter = null;
568 oldFormatter = this.formatter;
570 if (oldFormatter != null)
571 oldFormatter.uninstall();
573 this.formatter = formatter;
575 if (formatter != null)
576 formatter.install(this);
578 firePropertyChange("formatter", oldFormatter, formatter);
582 * Sets the factory from which this JFormattedTextField should obtain
583 * its formatters.
585 * @param factory the AbstractFormatterFactory that will be used to generate
586 * formatters for this JFormattedTextField
588 public void setFormatterFactory (AbstractFormatterFactory factory)
590 if (formatterFactory == factory)
591 return;
593 AbstractFormatterFactory oldFactory = formatterFactory;
594 formatterFactory = factory;
595 firePropertyChange("formatterFactory", oldFactory, factory);
597 // Now set the formatter according to our new factory.
598 if (formatterFactory != null)
599 setFormatter(formatterFactory.getFormatter(this));
600 else
601 setFormatter(null);
605 * Sets the value that will be formatted and displayed.
607 * @param newValue the value to be formatted and displayed
609 public void setValue (Object newValue)
611 if (value == newValue)
612 return;
614 Object oldValue = value;
615 value = newValue;
617 // If there is no formatterFactory then make one.
618 if (formatterFactory == null)
619 setFormatterFactory(createFormatterFactory(newValue));
621 // Set the formatter appropriately. This is because there may be a new
622 // formatterFactory from the line above, or we may want a new formatter
623 // depending on the type of newValue (or if newValue is null).
624 setFormatter (formatterFactory.getFormatter(this));
625 firePropertyChange("value", oldValue, newValue);
629 * A helper method that attempts to create a formatter factory that is
630 * suitable to format objects of the type like <code>value</code>.
632 * @param value an object which should be formatted by the formatter factory.
634 * @return a formatter factory able to format objects of the class of
635 * <code>value</code>
637 AbstractFormatterFactory createFormatterFactory(Object value)
639 AbstractFormatter formatter = null;
640 if (value instanceof Date)
641 formatter = new DateFormatter();
642 else if (value instanceof Number)
643 formatter = new NumberFormatter();
644 else
645 formatter = new DefaultFormatter();
646 return new DefaultFormatterFactory(formatter);