/* * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package javax.swing; import sun.swing.SwingUtilities2; import java.awt.*; import java.awt.event.*; import java.beans.*; import javax.swing.text.*; import javax.swing.plaf.*; import javax.swing.event.*; import javax.accessibility.*; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; import java.io.Serializable; /** * JTextField is a lightweight component that allows the editing * of a single line of text. * For information on and examples of using text fields, * see * How to Use Text Fields * in The Java Tutorial. * *

* JTextField is intended to be source-compatible * with java.awt.TextField where it is reasonable to do so. This * component has capabilities not found in the java.awt.TextField * class. The superclass should be consulted for additional capabilities. *

* JTextField has a method to establish the string used as the * command string for the action event that gets fired. The * java.awt.TextField used the text of the field as the command * string for the ActionEvent. * JTextField will use the command * string set with the setActionCommand method if not null, * otherwise it will use the text of the field as a compatibility with * java.awt.TextField. *

* The method setEchoChar and getEchoChar * are not provided directly to avoid a new implementation of a * pluggable look-and-feel inadvertently exposing password characters. * To provide password-like services a separate class JPasswordField * extends JTextField to provide this service with an independently * pluggable look-and-feel. *

* The java.awt.TextField could be monitored for changes by adding * a TextListener for TextEvent's. * In the JTextComponent based * components, changes are broadcasted from the model via a * DocumentEvent to DocumentListeners. * The DocumentEvent gives * the location of the change and the kind of change if desired. * The code fragment might look something like: *


 *     DocumentListener myListener = ??;
 *     JTextField myArea = ??;
 *     myArea.getDocument().addDocumentListener(myListener);
 * 
*

* The horizontal alignment of JTextField can be set to be left * justified, leading justified, centered, right justified or trailing justified. * Right/trailing justification is useful if the required size * of the field text is smaller than the size allocated to it. * This is determined by the setHorizontalAlignment * and getHorizontalAlignment methods. The default * is to be leading justified. *

* How the text field consumes VK_ENTER events depends * on whether the text field has any action listeners. * If so, then VK_ENTER results in the listeners * getting an ActionEvent, * and the VK_ENTER event is consumed. * This is compatible with how AWT text fields handle VK_ENTER events. * If the text field has no action listeners, then as of v 1.3 the VK_ENTER * event is not consumed. Instead, the bindings of ancestor components * are processed, which enables the default button feature of * JFC/Swing to work. *

* Customized fields can easily be created by extending the model and * changing the default model provided. For example, the following piece * of code will create a field that holds only upper case characters. It * will work even if text is pasted into from the clipboard or it is altered via * programmatic changes. *



 public class UpperCaseField extends JTextField {
 
     public UpperCaseField(int cols) {
         super(cols);
     }
 
     protected Document createDefaultModel() {
         return new UpperCaseDocument();
     }
 
     static class UpperCaseDocument extends PlainDocument {
 
         public void insertString(int offs, String str, AttributeSet a)
             throws BadLocationException {
 
             if (str == null) {
                 return;
             }
             char[] upper = str.toCharArray();
             for (int i = 0; i < upper.length; i++) {
                 upper[i] = Character.toUpperCase(upper[i]);
             }
             super.insertString(offs, new String(upper), a);
         }
     }
 }

 * 
*

* Warning: Swing is not thread safe. For more * information see Swing's Threading * Policy. *

* Warning: * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeansTM * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. * * @beaninfo * attribute: isContainer false * description: A component which allows for the editing of a single line of text. * * @author Timothy Prinzing * @see #setActionCommand * @see JPasswordField * @see #addActionListener */ public class JTextField extends JTextComponent implements SwingConstants { /** * Constructs a new TextField. A default model is created, * the initial string is null, * and the number of columns is set to 0. */ public JTextField() { this(null, null, 0); } /** * Constructs a new TextField initialized with the * specified text. A default model is created and the number of * columns is 0. * * @param text the text to be displayed, or null */ public JTextField(String text) { this(null, text, 0); } /** * Constructs a new empty TextField with the specified * number of columns. * A default model is created and the initial string is set to * null. * * @param columns the number of columns to use to calculate * the preferred width; if columns is set to zero, the * preferred width will be whatever naturally results from * the component implementation */ public JTextField(int columns) { this(null, null, columns); } /** * Constructs a new TextField initialized with the * specified text and columns. A default model is created. * * @param text the text to be displayed, or null * @param columns the number of columns to use to calculate * the preferred width; if columns is set to zero, the * preferred width will be whatever naturally results from * the component implementation */ public JTextField(String text, int columns) { this(null, text, columns); } /** * Constructs a new JTextField that uses the given text * storage model and the given number of columns. * This is the constructor through which the other constructors feed. * If the document is null, a default model is created. * * @param doc the text storage to use; if this is null, * a default will be provided by calling the * createDefaultModel method * @param text the initial string to display, or null * @param columns the number of columns to use to calculate * the preferred width >= 0; if columns * is set to zero, the preferred width will be whatever * naturally results from the component implementation * @exception IllegalArgumentException if columns < 0 */ public JTextField(Document doc, String text, int columns) { if (columns < 0) { throw new IllegalArgumentException("columns less than zero."); } visibility = new DefaultBoundedRangeModel(); visibility.addChangeListener(new ScrollRepainter()); this.columns = columns; if (doc == null) { doc = createDefaultModel(); } setDocument(doc); if (text != null) { setText(text); } } /** * Gets the class ID for a UI. * * @return the string "TextFieldUI" * @see JComponent#getUIClassID * @see UIDefaults#getUI */ public String getUIClassID() { return uiClassID; } /** * Associates the editor with a text document. * The currently registered factory is used to build a view for * the document, which gets displayed by the editor after revalidation. * A PropertyChange event ("document") is propagated to each listener. * * @param doc the document to display/edit * @see #getDocument * @beaninfo * description: the text document model * bound: true * expert: true */ public void setDocument(Document doc) { if (doc != null) { doc.putProperty("filterNewlines", Boolean.TRUE); } super.setDocument(doc); } /** * Calls to revalidate that come from within the * textfield itself will * be handled by validating the textfield, unless the textfield * is contained within a JViewport, * in which case this returns false. * * @return if the parent of this textfield is a JViewPort * return false, otherwise return true * * @see JComponent#revalidate * @see JComponent#isValidateRoot */ public boolean isValidateRoot() { return SwingUtilities.getParentViewport(this) == null; } /** * Returns the horizontal alignment of the text. * Valid keys are: *

* * @return the horizontal alignment */ public int getHorizontalAlignment() { return horizontalAlignment; } /** * Sets the horizontal alignment of the text. * Valid keys are: * * invalidate and repaint are called when the * alignment is set, * and a PropertyChange event ("horizontalAlignment") is fired. * * @param alignment the alignment * @exception IllegalArgumentException if alignment * is not a valid key * @beaninfo * preferred: true * bound: true * description: Set the field alignment to LEFT, CENTER, RIGHT, * LEADING (the default) or TRAILING * enum: LEFT JTextField.LEFT CENTER JTextField.CENTER RIGHT JTextField.RIGHT * LEADING JTextField.LEADING TRAILING JTextField.TRAILING */ public void setHorizontalAlignment(int alignment) { if (alignment == horizontalAlignment) return; int oldValue = horizontalAlignment; if ((alignment == LEFT) || (alignment == CENTER) || (alignment == RIGHT)|| (alignment == LEADING) || (alignment == TRAILING)) { horizontalAlignment = alignment; } else { throw new IllegalArgumentException("horizontalAlignment"); } firePropertyChange("horizontalAlignment", oldValue, horizontalAlignment); invalidate(); repaint(); } /** * Creates the default implementation of the model * to be used at construction if one isn't explicitly * given. An instance of PlainDocument is returned. * * @return the default model implementation */ protected Document createDefaultModel() { return new PlainDocument(); } /** * Returns the number of columns in this TextField. * * @return the number of columns >= 0 */ public int getColumns() { return columns; } /** * Sets the number of columns in this TextField, * and then invalidate the layout. * * @param columns the number of columns >= 0 * @exception IllegalArgumentException if columns * is less than 0 * @beaninfo * description: the number of columns preferred for display */ public void setColumns(int columns) { int oldVal = this.columns; if (columns < 0) { throw new IllegalArgumentException("columns less than zero."); } if (columns != oldVal) { this.columns = columns; invalidate(); } } /** * Returns the column width. * The meaning of what a column is can be considered a fairly weak * notion for some fonts. This method is used to define the width * of a column. By default this is defined to be the width of the * character m for the font used. This method can be * redefined to be some alternative amount * * @return the column width >= 1 */ protected int getColumnWidth() { if (columnWidth == 0) { FontMetrics metrics = getFontMetrics(getFont()); columnWidth = metrics.charWidth('m'); } return columnWidth; } /** * Returns the preferred size Dimensions needed for this * TextField. If a non-zero number of columns has been * set, the width is set to the columns multiplied by * the column width. * * @return the dimension of this textfield */ public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); if (columns != 0) { Insets insets = getInsets(); size.width = columns * getColumnWidth() + insets.left + insets.right; } return size; } /** * Sets the current font. This removes cached row height and column * width so the new font will be reflected. * revalidate is called after setting the font. * * @param f the new font */ public void setFont(Font f) { super.setFont(f); columnWidth = 0; } /** * Adds the specified action listener to receive * action events from this textfield. * * @param l the action listener to be added */ public synchronized void addActionListener(ActionListener l) { listenerList.add(ActionListener.class, l); } /** * Removes the specified action listener so that it no longer * receives action events from this textfield. * * @param l the action listener to be removed */ public synchronized void removeActionListener(ActionListener l) { if ((l != null) && (getAction() == l)) { setAction(null); } else { listenerList.remove(ActionListener.class, l); } } /** * Returns an array of all the ActionListeners added * to this JTextField with addActionListener(). * * @return all of the ActionListeners added or an empty * array if no listeners have been added * @since 1.4 */ public synchronized ActionListener[] getActionListeners() { return listenerList.getListeners(ActionListener.class); } /** * Notifies all listeners that have registered interest for * notification on this event type. The event instance * is lazily created. * The listener list is processed in last to * first order. * @see EventListenerList */ protected void fireActionPerformed() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); int modifiers = 0; AWTEvent currentEvent = EventQueue.getCurrentEvent(); if (currentEvent instanceof InputEvent) { modifiers = ((InputEvent)currentEvent).getModifiers(); } else if (currentEvent instanceof ActionEvent) { modifiers = ((ActionEvent)currentEvent).getModifiers(); } ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, (command != null) ? command : getText(), EventQueue.getMostRecentEventTime(), modifiers); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==ActionListener.class) { ((ActionListener)listeners[i+1]).actionPerformed(e); } } } /** * Sets the command string used for action events. * * @param command the command string */ public void setActionCommand(String command) { this.command = command; } private Action action; private PropertyChangeListener actionPropertyChangeListener; /** * Sets the Action for the ActionEvent source. * The new Action replaces * any previously set Action but does not affect * ActionListeners independently * added with addActionListener. * If the Action is already a registered * ActionListener * for the ActionEvent source, it is not re-registered. *

* Setting the Action results in immediately changing * all the properties described in * Swing Components Supporting Action. * Subsequently, the textfield's properties are automatically updated * as the Action's properties change. *

* This method uses three other methods to set * and help track the Action's property values. * It uses the configurePropertiesFromAction method * to immediately change the textfield's properties. * To track changes in the Action's property values, * this method registers the PropertyChangeListener * returned by createActionPropertyChangeListener. The * default {@code PropertyChangeListener} invokes the * {@code actionPropertyChanged} method when a property in the * {@code Action} changes. * * @param a the Action for the JTextField, * or null * @since 1.3 * @see Action * @see #getAction * @see #configurePropertiesFromAction * @see #createActionPropertyChangeListener * @see #actionPropertyChanged * @beaninfo * bound: true * attribute: visualUpdate true * description: the Action instance connected with this ActionEvent source */ public void setAction(Action a) { Action oldValue = getAction(); if (action==null || !action.equals(a)) { action = a; if (oldValue!=null) { removeActionListener(oldValue); oldValue.removePropertyChangeListener(actionPropertyChangeListener); actionPropertyChangeListener = null; } configurePropertiesFromAction(action); if (action!=null) { // Don't add if it is already a listener if (!isListener(ActionListener.class, action)) { addActionListener(action); } // Reverse linkage: actionPropertyChangeListener = createActionPropertyChangeListener(action); action.addPropertyChangeListener(actionPropertyChangeListener); } firePropertyChange("action", oldValue, action); } } private boolean isListener(Class c, ActionListener a) { boolean isListener = false; Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==c && listeners[i+1]==a) { isListener=true; } } return isListener; } /** * Returns the currently set Action for this * ActionEvent source, or null * if no Action is set. * * @return the Action for this ActionEvent source, * or null * @since 1.3 * @see Action * @see #setAction */ public Action getAction() { return action; } /** * Sets the properties on this textfield to match those in the specified * Action. Refer to * Swing Components Supporting Action for more * details as to which properties this sets. * * @param a the Action from which to get the properties, * or null * @since 1.3 * @see Action * @see #setAction */ protected void configurePropertiesFromAction(Action a) { AbstractAction.setEnabledFromAction(this, a); AbstractAction.setToolTipTextFromAction(this, a); setActionCommandFromAction(a); } /** * Updates the textfield's state in response to property changes in * associated action. This method is invoked from the * {@code PropertyChangeListener} returned from * {@code createActionPropertyChangeListener}. Subclasses do not normally * need to invoke this. Subclasses that support additional {@code Action} * properties should override this and * {@code configurePropertiesFromAction}. *

* Refer to the table at * Swing Components Supporting Action for a list of * the properties this method sets. * * @param action the Action associated with this textfield * @param propertyName the name of the property that changed * @since 1.6 * @see Action * @see #configurePropertiesFromAction */ protected void actionPropertyChanged(Action action, String propertyName) { if (propertyName == Action.ACTION_COMMAND_KEY) { setActionCommandFromAction(action); } else if (propertyName == "enabled") { AbstractAction.setEnabledFromAction(this, action); } else if (propertyName == Action.SHORT_DESCRIPTION) { AbstractAction.setToolTipTextFromAction(this, action); } } private void setActionCommandFromAction(Action action) { setActionCommand((action == null) ? null : (String)action.getValue(Action.ACTION_COMMAND_KEY)); } /** * Creates and returns a PropertyChangeListener that is * responsible for listening for changes from the specified * Action and updating the appropriate properties. *

* Warning: If you subclass this do not create an anonymous * inner class. If you do the lifetime of the textfield will be tied to * that of the Action. * * @param a the textfield's action * @since 1.3 * @see Action * @see #setAction */ protected PropertyChangeListener createActionPropertyChangeListener(Action a) { return new TextFieldActionPropertyChangeListener(this, a); } private static class TextFieldActionPropertyChangeListener extends ActionPropertyChangeListener { TextFieldActionPropertyChangeListener(JTextField tf, Action a) { super(tf, a); } protected void actionPropertyChanged(JTextField textField, Action action, PropertyChangeEvent e) { if (AbstractAction.shouldReconfigure(e)) { textField.configurePropertiesFromAction(action); } else { textField.actionPropertyChanged(action, e.getPropertyName()); } } } /** * Fetches the command list for the editor. This is * the list of commands supported by the plugged-in UI * augmented by the collection of commands that the * editor itself supports. These are useful for binding * to events, such as in a keymap. * * @return the command list */ public Action[] getActions() { return TextAction.augmentList(super.getActions(), defaultActions); } /** * Processes action events occurring on this textfield by * dispatching them to any registered ActionListener objects. * This is normally called by the controller registered with * textfield. */ public void postActionEvent() { fireActionPerformed(); } // --- Scrolling support ----------------------------------- /** * Gets the visibility of the text field. This can * be adjusted to change the location of the visible * area if the size of the field is greater than * the area that was allocated to the field. * *

* The fields look-and-feel implementation manages * the values of the minimum, maximum, and extent * properties on the BoundedRangeModel. * * @return the visibility * @see BoundedRangeModel */ public BoundedRangeModel getHorizontalVisibility() { return visibility; } /** * Gets the scroll offset, in pixels. * * @return the offset >= 0 */ public int getScrollOffset() { return visibility.getValue(); } /** * Sets the scroll offset, in pixels. * * @param scrollOffset the offset >= 0 */ public void setScrollOffset(int scrollOffset) { visibility.setValue(scrollOffset); } /** * Scrolls the field left or right. * * @param r the region to scroll */ public void scrollRectToVisible(Rectangle r) { // convert to coordinate system of the bounded range Insets i = getInsets(); int x0 = r.x + visibility.getValue() - i.left; int x1 = x0 + r.width; if (x0 < visibility.getValue()) { // Scroll to the left visibility.setValue(x0); } else if(x1 > visibility.getValue() + visibility.getExtent()) { // Scroll to the right visibility.setValue(x1 - visibility.getExtent()); } } /** * Returns true if the receiver has an ActionListener * installed. */ boolean hasActionListener() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==ActionListener.class) { return true; } } return false; } // --- variables ------------------------------------------- /** * Name of the action to send notification that the * contents of the field have been accepted. Typically * this is bound to a carriage-return. */ public static final String notifyAction = "notify-field-accept"; private BoundedRangeModel visibility; private int horizontalAlignment = LEADING; private int columns; private int columnWidth; private String command; private static final Action[] defaultActions = { new NotifyAction() }; /** * @see #getUIClassID * @see #readObject */ private static final String uiClassID = "TextFieldUI"; // --- Action implementations ----------------------------------- // Note that JFormattedTextField.CommitAction extends this static class NotifyAction extends TextAction { NotifyAction() { super(notifyAction); } public void actionPerformed(ActionEvent e) { JTextComponent target = getFocusedComponent(); if (target instanceof JTextField) { JTextField field = (JTextField) target; field.postActionEvent(); } } public boolean isEnabled() { JTextComponent target = getFocusedComponent(); if (target instanceof JTextField) { return ((JTextField)target).hasActionListener(); } return false; } } class ScrollRepainter implements ChangeListener, Serializable { public void stateChanged(ChangeEvent e) { repaint(); } } /** * See readObject and writeObject in * JComponent for more * information about serialization in Swing. */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); if (getUIClassID().equals(uiClassID)) { byte count = JComponent.getWriteObjCounter(this); JComponent.setWriteObjCounter(this, --count); if (count == 0 && ui != null) { ui.installUI(this); } } } /** * Returns a string representation of this JTextField. * This method is intended to be used only for debugging purposes, * and the content and format of the returned string may vary between * implementations. The returned string may be empty but may not * be null. * * @return a string representation of this JTextField */ protected String paramString() { String horizontalAlignmentString; if (horizontalAlignment == LEFT) { horizontalAlignmentString = "LEFT"; } else if (horizontalAlignment == CENTER) { horizontalAlignmentString = "CENTER"; } else if (horizontalAlignment == RIGHT) { horizontalAlignmentString = "RIGHT"; } else if (horizontalAlignment == LEADING) { horizontalAlignmentString = "LEADING"; } else if (horizontalAlignment == TRAILING) { horizontalAlignmentString = "TRAILING"; } else horizontalAlignmentString = ""; String commandString = (command != null ? command : ""); return super.paramString() + ",columns=" + columns + ",columnWidth=" + columnWidth + ",command=" + commandString + ",horizontalAlignment=" + horizontalAlignmentString; } ///////////////// // Accessibility support //////////////// /** * Gets the AccessibleContext associated with this * JTextField. For JTextFields, * the AccessibleContext takes the form of an * AccessibleJTextField. * A new AccessibleJTextField instance is created * if necessary. * * @return an AccessibleJTextField that serves as the * AccessibleContext of this JTextField */ public AccessibleContext getAccessibleContext() { if (accessibleContext == null) { accessibleContext = new AccessibleJTextField(); } return accessibleContext; } /** * This class implements accessibility support for the * JTextField class. It provides an implementation of the * Java Accessibility API appropriate to text field user-interface * elements. *

* Warning: * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeansTM * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. */ protected class AccessibleJTextField extends AccessibleJTextComponent { /** * Gets the state set of this object. * * @return an instance of AccessibleStateSet describing the states * of the object * @see AccessibleState */ public AccessibleStateSet getAccessibleStateSet() { AccessibleStateSet states = super.getAccessibleStateSet(); states.add(AccessibleState.SINGLE_LINE); return states; } } }