001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing;
026
027 import java.awt.*;
028 import java.awt.event.*;
029 import java.beans.*;
030 import javax.swing.text.*;
031 import javax.swing.plaf.*;
032 import javax.swing.event.*;
033 import javax.accessibility.*;
034
035 import java.io.ObjectOutputStream;
036 import java.io.ObjectInputStream;
037 import java.io.IOException;
038 import java.io.Serializable;
039
040 /**
041 * <code>JTextField</code> is a lightweight component that allows the editing
042 * of a single line of text.
043 * For information on and examples of using text fields,
044 * see
045 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/textfield.html">How to Use Text Fields</a>
046 * in <em>The Java Tutorial.</em>
047 *
048 * <p>
049 * <code>JTextField</code> is intended to be source-compatible
050 * with <code>java.awt.TextField</code> where it is reasonable to do so. This
051 * component has capabilities not found in the <code>java.awt.TextField</code>
052 * class. The superclass should be consulted for additional capabilities.
053 * <p>
054 * <code>JTextField</code> has a method to establish the string used as the
055 * command string for the action event that gets fired. The
056 * <code>java.awt.TextField</code> used the text of the field as the command
057 * string for the <code>ActionEvent</code>.
058 * <code>JTextField</code> will use the command
059 * string set with the <code>setActionCommand</code> method if not <code>null</code>,
060 * otherwise it will use the text of the field as a compatibility with
061 * <code>java.awt.TextField</code>.
062 * <p>
063 * The method <code>setEchoChar</code> and <code>getEchoChar</code>
064 * are not provided directly to avoid a new implementation of a
065 * pluggable look-and-feel inadvertently exposing password characters.
066 * To provide password-like services a separate class <code>JPasswordField</code>
067 * extends <code>JTextField</code> to provide this service with an independently
068 * pluggable look-and-feel.
069 * <p>
070 * The <code>java.awt.TextField</code> could be monitored for changes by adding
071 * a <code>TextListener</code> for <code>TextEvent</code>'s.
072 * In the <code>JTextComponent</code> based
073 * components, changes are broadcasted from the model via a
074 * <code>DocumentEvent</code> to <code>DocumentListeners</code>.
075 * The <code>DocumentEvent</code> gives
076 * the location of the change and the kind of change if desired.
077 * The code fragment might look something like:
078 * <pre><code>
079 * DocumentListener myListener = ??;
080 * JTextField myArea = ??;
081 * myArea.getDocument().addDocumentListener(myListener);
082 * </code></pre>
083 * <p>
084 * The horizontal alignment of <code>JTextField</code> can be set to be left
085 * justified, leading justified, centered, right justified or trailing justified.
086 * Right/trailing justification is useful if the required size
087 * of the field text is smaller than the size allocated to it.
088 * This is determined by the <code>setHorizontalAlignment</code>
089 * and <code>getHorizontalAlignment</code> methods. The default
090 * is to be leading justified.
091 * <p>
092 * How the text field consumes VK_ENTER events depends
093 * on whether the text field has any action listeners.
094 * If so, then VK_ENTER results in the listeners
095 * getting an ActionEvent,
096 * and the VK_ENTER event is consumed.
097 * This is compatible with how AWT text fields handle VK_ENTER events.
098 * If the text field has no action listeners, then as of v 1.3 the VK_ENTER
099 * event is not consumed. Instead, the bindings of ancestor components
100 * are processed, which enables the default button feature of
101 * JFC/Swing to work.
102 * <p>
103 * Customized fields can easily be created by extending the model and
104 * changing the default model provided. For example, the following piece
105 * of code will create a field that holds only upper case characters. It
106 * will work even if text is pasted into from the clipboard or it is altered via
107 * programmatic changes.
108 * <pre><code>
109
110 public class UpperCaseField extends JTextField {
111
112 public UpperCaseField(int cols) {
113 super(cols);
114 }
115
116 protected Document createDefaultModel() {
117 return new UpperCaseDocument();
118 }
119
120 static class UpperCaseDocument extends PlainDocument {
121
122 public void insertString(int offs, String str, AttributeSet a)
123 throws BadLocationException {
124
125 if (str == null) {
126 return;
127 }
128 char[] upper = str.toCharArray();
129 for (int i = 0; i < upper.length; i++) {
130 upper[i] = Character.toUpperCase(upper[i]);
131 }
132 super.insertString(offs, new String(upper), a);
133 }
134 }
135 }
136
137 * </code></pre>
138 * <p>
139 * <strong>Warning:</strong> Swing is not thread safe. For more
140 * information see <a
141 * href="package-summary.html#threading">Swing's Threading
142 * Policy</a>.
143 * <p>
144 * <strong>Warning:</strong>
145 * Serialized objects of this class will not be compatible with
146 * future Swing releases. The current serialization support is
147 * appropriate for short term storage or RMI between applications running
148 * the same version of Swing. As of 1.4, support for long term storage
149 * of all JavaBeans<sup><font size="-2">TM</font></sup>
150 * has been added to the <code>java.beans</code> package.
151 * Please see {@link java.beans.XMLEncoder}.
152 *
153 * @beaninfo
154 * attribute: isContainer false
155 * description: A component which allows for the editing of a single line of text.
156 *
157 * @author Timothy Prinzing
158 * @version 1.101 05/05/07
159 * @see #setActionCommand
160 * @see JPasswordField
161 * @see #addActionListener
162 */
163 public class JTextField extends JTextComponent implements
164 SwingConstants {
165
166 /**
167 * Constructs a new <code>TextField</code>. A default model is created,
168 * the initial string is <code>null</code>,
169 * and the number of columns is set to 0.
170 */
171 public JTextField() {
172 this (null, null, 0);
173 }
174
175 /**
176 * Constructs a new <code>TextField</code> initialized with the
177 * specified text. A default model is created and the number of
178 * columns is 0.
179 *
180 * @param text the text to be displayed, or <code>null</code>
181 */
182 public JTextField(String text) {
183 this (null, text, 0);
184 }
185
186 /**
187 * Constructs a new empty <code>TextField</code> with the specified
188 * number of columns.
189 * A default model is created and the initial string is set to
190 * <code>null</code>.
191 *
192 * @param columns the number of columns to use to calculate
193 * the preferred width; if columns is set to zero, the
194 * preferred width will be whatever naturally results from
195 * the component implementation
196 */
197 public JTextField(int columns) {
198 this (null, null, columns);
199 }
200
201 /**
202 * Constructs a new <code>TextField</code> initialized with the
203 * specified text and columns. A default model is created.
204 *
205 * @param text the text to be displayed, or <code>null</code>
206 * @param columns the number of columns to use to calculate
207 * the preferred width; if columns is set to zero, the
208 * preferred width will be whatever naturally results from
209 * the component implementation
210 */
211 public JTextField(String text, int columns) {
212 this (null, text, columns);
213 }
214
215 /**
216 * Constructs a new <code>JTextField</code> that uses the given text
217 * storage model and the given number of columns.
218 * This is the constructor through which the other constructors feed.
219 * If the document is <code>null</code>, a default model is created.
220 *
221 * @param doc the text storage to use; if this is <code>null</code>,
222 * a default will be provided by calling the
223 * <code>createDefaultModel</code> method
224 * @param text the initial string to display, or <code>null</code>
225 * @param columns the number of columns to use to calculate
226 * the preferred width >= 0; if <code>columns</code>
227 * is set to zero, the preferred width will be whatever
228 * naturally results from the component implementation
229 * @exception IllegalArgumentException if <code>columns</code> < 0
230 */
231 public JTextField(Document doc, String text, int columns) {
232 if (columns < 0) {
233 throw new IllegalArgumentException(
234 "columns less than zero.");
235 }
236 visibility = new DefaultBoundedRangeModel();
237 visibility.addChangeListener(new ScrollRepainter());
238 this .columns = columns;
239 if (doc == null) {
240 doc = createDefaultModel();
241 }
242 setDocument(doc);
243 if (text != null) {
244 setText(text);
245 }
246 }
247
248 /**
249 * Gets the class ID for a UI.
250 *
251 * @return the string "TextFieldUI"
252 * @see JComponent#getUIClassID
253 * @see UIDefaults#getUI
254 */
255 public String getUIClassID() {
256 return uiClassID;
257 }
258
259 /**
260 * Associates the editor with a text document.
261 * The currently registered factory is used to build a view for
262 * the document, which gets displayed by the editor after revalidation.
263 * A PropertyChange event ("document") is propagated to each listener.
264 *
265 * @param doc the document to display/edit
266 * @see #getDocument
267 * @beaninfo
268 * description: the text document model
269 * bound: true
270 * expert: true
271 */
272 public void setDocument(Document doc) {
273 if (doc != null) {
274 doc.putProperty("filterNewlines", Boolean.TRUE);
275 }
276 super .setDocument(doc);
277 }
278
279 /**
280 * Calls to <code>revalidate</code> that come from within the
281 * textfield itself will
282 * be handled by validating the textfield, unless the textfield
283 * is contained within a <code>JViewport</code>,
284 * in which case this returns false.
285 *
286 * @return if the parent of this textfield is a <code>JViewPort</code>
287 * return false, otherwise return true
288 *
289 * @see JComponent#revalidate
290 * @see JComponent#isValidateRoot
291 */
292 public boolean isValidateRoot() {
293 Component parent = getParent();
294 if (parent instanceof JViewport) {
295 return false;
296 }
297 return true;
298 }
299
300 /**
301 * Returns the horizontal alignment of the text.
302 * Valid keys are:
303 * <ul>
304 * <li><code>JTextField.LEFT</code>
305 * <li><code>JTextField.CENTER</code>
306 * <li><code>JTextField.RIGHT</code>
307 * <li><code>JTextField.LEADING</code>
308 * <li><code>JTextField.TRAILING</code>
309 * </ul>
310 *
311 * @return the horizontal alignment
312 */
313 public int getHorizontalAlignment() {
314 return horizontalAlignment;
315 }
316
317 /**
318 * Sets the horizontal alignment of the text.
319 * Valid keys are:
320 * <ul>
321 * <li><code>JTextField.LEFT</code>
322 * <li><code>JTextField.CENTER</code>
323 * <li><code>JTextField.RIGHT</code>
324 * <li><code>JTextField.LEADING</code>
325 * <li><code>JTextField.TRAILING</code>
326 * </ul>
327 * <code>invalidate</code> and <code>repaint</code> are called when the
328 * alignment is set,
329 * and a <code>PropertyChange</code> event ("horizontalAlignment") is fired.
330 *
331 * @param alignment the alignment
332 * @exception IllegalArgumentException if <code>alignment</code>
333 * is not a valid key
334 * @beaninfo
335 * preferred: true
336 * bound: true
337 * description: Set the field alignment to LEFT, CENTER, RIGHT,
338 * LEADING (the default) or TRAILING
339 * enum: LEFT JTextField.LEFT CENTER JTextField.CENTER RIGHT JTextField.RIGHT
340 * LEADING JTextField.LEADING TRAILING JTextField.TRAILING
341 */
342 public void setHorizontalAlignment(int alignment) {
343 if (alignment == horizontalAlignment)
344 return;
345 int oldValue = horizontalAlignment;
346 if ((alignment == LEFT) || (alignment == CENTER)
347 || (alignment == RIGHT) || (alignment == LEADING)
348 || (alignment == TRAILING)) {
349 horizontalAlignment = alignment;
350 } else {
351 throw new IllegalArgumentException("horizontalAlignment");
352 }
353 firePropertyChange("horizontalAlignment", oldValue,
354 horizontalAlignment);
355 invalidate();
356 repaint();
357 }
358
359 /**
360 * Creates the default implementation of the model
361 * to be used at construction if one isn't explicitly
362 * given. An instance of <code>PlainDocument</code> is returned.
363 *
364 * @return the default model implementation
365 */
366 protected Document createDefaultModel() {
367 return new PlainDocument();
368 }
369
370 /**
371 * Returns the number of columns in this <code>TextField</code>.
372 *
373 * @return the number of columns >= 0
374 */
375 public int getColumns() {
376 return columns;
377 }
378
379 /**
380 * Sets the number of columns in this <code>TextField</code>,
381 * and then invalidate the layout.
382 *
383 * @param columns the number of columns >= 0
384 * @exception IllegalArgumentException if <code>columns</code>
385 * is less than 0
386 * @beaninfo
387 * description: the number of columns preferred for display
388 */
389 public void setColumns(int columns) {
390 int oldVal = this .columns;
391 if (columns < 0) {
392 throw new IllegalArgumentException(
393 "columns less than zero.");
394 }
395 if (columns != oldVal) {
396 this .columns = columns;
397 invalidate();
398 }
399 }
400
401 /**
402 * Returns the column width.
403 * The meaning of what a column is can be considered a fairly weak
404 * notion for some fonts. This method is used to define the width
405 * of a column. By default this is defined to be the width of the
406 * character <em>m</em> for the font used. This method can be
407 * redefined to be some alternative amount
408 *
409 * @return the column width >= 1
410 */
411 protected int getColumnWidth() {
412 if (columnWidth == 0) {
413 FontMetrics metrics = getFontMetrics(getFont());
414 columnWidth = metrics.charWidth('m');
415 }
416 return columnWidth;
417 }
418
419 /**
420 * Returns the preferred size <code>Dimensions</code> needed for this
421 * <code>TextField</code>. If a non-zero number of columns has been
422 * set, the width is set to the columns multiplied by
423 * the column width.
424 *
425 * @return the dimension of this textfield
426 */
427 public Dimension getPreferredSize() {
428 Dimension size = super .getPreferredSize();
429 if (columns != 0) {
430 Insets insets = getInsets();
431 size.width = columns * getColumnWidth() + insets.left
432 + insets.right;
433 }
434 return size;
435 }
436
437 /**
438 * Sets the current font. This removes cached row height and column
439 * width so the new font will be reflected.
440 * <code>revalidate</code> is called after setting the font.
441 *
442 * @param f the new font
443 */
444 public void setFont(Font f) {
445 super .setFont(f);
446 columnWidth = 0;
447 }
448
449 /**
450 * Adds the specified action listener to receive
451 * action events from this textfield.
452 *
453 * @param l the action listener to be added
454 */
455 public synchronized void addActionListener(ActionListener l) {
456 listenerList.add(ActionListener.class, l);
457 }
458
459 /**
460 * Removes the specified action listener so that it no longer
461 * receives action events from this textfield.
462 *
463 * @param l the action listener to be removed
464 */
465 public synchronized void removeActionListener(ActionListener l) {
466 if ((l != null) && (getAction() == l)) {
467 setAction(null);
468 } else {
469 listenerList.remove(ActionListener.class, l);
470 }
471 }
472
473 /**
474 * Returns an array of all the <code>ActionListener</code>s added
475 * to this JTextField with addActionListener().
476 *
477 * @return all of the <code>ActionListener</code>s added or an empty
478 * array if no listeners have been added
479 * @since 1.4
480 */
481 public synchronized ActionListener[] getActionListeners() {
482 return (ActionListener[]) listenerList
483 .getListeners(ActionListener.class);
484 }
485
486 /**
487 * Notifies all listeners that have registered interest for
488 * notification on this event type. The event instance
489 * is lazily created.
490 * The listener list is processed in last to
491 * first order.
492 * @see EventListenerList
493 */
494 protected void fireActionPerformed() {
495 // Guaranteed to return a non-null array
496 Object[] listeners = listenerList.getListenerList();
497 int modifiers = 0;
498 AWTEvent currentEvent = EventQueue.getCurrentEvent();
499 if (currentEvent instanceof InputEvent) {
500 modifiers = ((InputEvent) currentEvent).getModifiers();
501 } else if (currentEvent instanceof ActionEvent) {
502 modifiers = ((ActionEvent) currentEvent).getModifiers();
503 }
504 ActionEvent e = new ActionEvent(this ,
505 ActionEvent.ACTION_PERFORMED,
506 (command != null) ? command : getText(), EventQueue
507 .getMostRecentEventTime(), modifiers);
508
509 // Process the listeners last to first, notifying
510 // those that are interested in this event
511 for (int i = listeners.length - 2; i >= 0; i -= 2) {
512 if (listeners[i] == ActionListener.class) {
513 ((ActionListener) listeners[i + 1]).actionPerformed(e);
514 }
515 }
516 }
517
518 /**
519 * Sets the command string used for action events.
520 *
521 * @param command the command string
522 */
523 public void setActionCommand(String command) {
524 this .command = command;
525 }
526
527 private Action action;
528 private PropertyChangeListener actionPropertyChangeListener;
529
530 /**
531 * Sets the <code>Action</code> for the <code>ActionEvent</code> source.
532 * The new <code>Action</code> replaces
533 * any previously set <code>Action</code> but does not affect
534 * <code>ActionListeners</code> independently
535 * added with <code>addActionListener</code>.
536 * If the <code>Action</code> is already a registered
537 * <code>ActionListener</code>
538 * for the <code>ActionEvent</code> source, it is not re-registered.
539 * <p>
540 * Setting the <code>Action</code> results in immediately changing
541 * all the properties described in <a href="Action.html#buttonActions">
542 * Swing Components Supporting <code>Action</code></a>.
543 * Subsequently, the textfield's properties are automatically updated
544 * as the <code>Action</code>'s properties change.
545 * <p>
546 * This method uses three other methods to set
547 * and help track the <code>Action</code>'s property values.
548 * It uses the <code>configurePropertiesFromAction</code> method
549 * to immediately change the textfield's properties.
550 * To track changes in the <code>Action</code>'s property values,
551 * this method registers the <code>PropertyChangeListener</code>
552 * returned by <code>createActionPropertyChangeListener</code>. The
553 * default {@code PropertyChangeListener} invokes the
554 * {@code actionPropertyChanged} method when a property in the
555 * {@code Action} changes.
556 *
557 * @param a the <code>Action</code> for the <code>JTextField</code>,
558 * or <code>null</code>
559 * @since 1.3
560 * @see Action
561 * @see #getAction
562 * @see #configurePropertiesFromAction
563 * @see #createActionPropertyChangeListener
564 * @see #actionPropertyChanged
565 * @beaninfo
566 * bound: true
567 * attribute: visualUpdate true
568 * description: the Action instance connected with this ActionEvent source
569 */
570 public void setAction(Action a) {
571 Action oldValue = getAction();
572 if (action == null || !action.equals(a)) {
573 action = a;
574 if (oldValue != null) {
575 removeActionListener(oldValue);
576 oldValue
577 .removePropertyChangeListener(actionPropertyChangeListener);
578 actionPropertyChangeListener = null;
579 }
580 configurePropertiesFromAction(action);
581 if (action != null) {
582 // Don't add if it is already a listener
583 if (!isListener(ActionListener.class, action)) {
584 addActionListener(action);
585 }
586 // Reverse linkage:
587 actionPropertyChangeListener = createActionPropertyChangeListener(action);
588 action
589 .addPropertyChangeListener(actionPropertyChangeListener);
590 }
591 firePropertyChange("action", oldValue, action);
592 }
593 }
594
595 private boolean isListener(Class c, ActionListener a) {
596 boolean isListener = false;
597 Object[] listeners = listenerList.getListenerList();
598 for (int i = listeners.length - 2; i >= 0; i -= 2) {
599 if (listeners[i] == c && listeners[i + 1] == a) {
600 isListener = true;
601 }
602 }
603 return isListener;
604 }
605
606 /**
607 * Returns the currently set <code>Action</code> for this
608 * <code>ActionEvent</code> source, or <code>null</code>
609 * if no <code>Action</code> is set.
610 *
611 * @return the <code>Action</code> for this <code>ActionEvent</code> source,
612 * or <code>null</code>
613 * @since 1.3
614 * @see Action
615 * @see #setAction
616 */
617 public Action getAction() {
618 return action;
619 }
620
621 /**
622 * Sets the properties on this textfield to match those in the specified
623 * <code>Action</code>. Refer to <a href="Action.html#buttonActions">
624 * Swing Components Supporting <code>Action</code></a> for more
625 * details as to which properties this sets.
626 *
627 * @param a the <code>Action</code> from which to get the properties,
628 * or <code>null</code>
629 * @since 1.3
630 * @see Action
631 * @see #setAction
632 */
633 protected void configurePropertiesFromAction(Action a) {
634 AbstractAction.setEnabledFromAction(this , a);
635 AbstractAction.setToolTipTextFromAction(this , a);
636 setActionCommandFromAction(a);
637 }
638
639 /**
640 * Updates the textfield's state in response to property changes in
641 * associated action. This method is invoked from the
642 * {@code PropertyChangeListener} returned from
643 * {@code createActionPropertyChangeListener}. Subclasses do not normally
644 * need to invoke this. Subclasses that support additional {@code Action}
645 * properties should override this and
646 * {@code configurePropertiesFromAction}.
647 * <p>
648 * Refer to the table at <a href="Action.html#buttonActions">
649 * Swing Components Supporting <code>Action</code></a> for a list of
650 * the properties this method sets.
651 *
652 * @param action the <code>Action</code> associated with this textfield
653 * @param propertyName the name of the property that changed
654 * @since 1.6
655 * @see Action
656 * @see #configurePropertiesFromAction
657 */
658 protected void actionPropertyChanged(Action action,
659 String propertyName) {
660 if (propertyName == Action.ACTION_COMMAND_KEY) {
661 setActionCommandFromAction(action);
662 } else if (propertyName == "enabled") {
663 AbstractAction.setEnabledFromAction(this , action);
664 } else if (propertyName == Action.SHORT_DESCRIPTION) {
665 AbstractAction.setToolTipTextFromAction(this , action);
666 }
667 }
668
669 private void setActionCommandFromAction(Action action) {
670 setActionCommand((action == null) ? null : (String) action
671 .getValue(Action.ACTION_COMMAND_KEY));
672 }
673
674 /**
675 * Creates and returns a <code>PropertyChangeListener</code> that is
676 * responsible for listening for changes from the specified
677 * <code>Action</code> and updating the appropriate properties.
678 * <p>
679 * <b>Warning:</b> If you subclass this do not create an anonymous
680 * inner class. If you do the lifetime of the textfield will be tied to
681 * that of the <code>Action</code>.
682 *
683 * @param a the textfield's action
684 * @since 1.3
685 * @see Action
686 * @see #setAction
687 */
688 protected PropertyChangeListener createActionPropertyChangeListener(
689 Action a) {
690 return new TextFieldActionPropertyChangeListener(this , a);
691 }
692
693 private static class TextFieldActionPropertyChangeListener extends
694 ActionPropertyChangeListener<JTextField> {
695 TextFieldActionPropertyChangeListener(JTextField tf, Action a) {
696 super (tf, a);
697 }
698
699 protected void actionPropertyChanged(JTextField textField,
700 Action action, PropertyChangeEvent e) {
701 if (AbstractAction.shouldReconfigure(e)) {
702 textField.configurePropertiesFromAction(action);
703 } else {
704 textField.actionPropertyChanged(action, e
705 .getPropertyName());
706 }
707 }
708 }
709
710 /**
711 * Fetches the command list for the editor. This is
712 * the list of commands supported by the plugged-in UI
713 * augmented by the collection of commands that the
714 * editor itself supports. These are useful for binding
715 * to events, such as in a keymap.
716 *
717 * @return the command list
718 */
719 public Action[] getActions() {
720 return TextAction.augmentList(super .getActions(),
721 defaultActions);
722 }
723
724 /**
725 * Processes action events occurring on this textfield by
726 * dispatching them to any registered <code>ActionListener</code> objects.
727 * This is normally called by the controller registered with
728 * textfield.
729 */
730 public void postActionEvent() {
731 fireActionPerformed();
732 }
733
734 // --- Scrolling support -----------------------------------
735
736 /**
737 * Gets the visibility of the text field. This can
738 * be adjusted to change the location of the visible
739 * area if the size of the field is greater than
740 * the area that was allocated to the field.
741 *
742 * <p>
743 * The fields look-and-feel implementation manages
744 * the values of the minimum, maximum, and extent
745 * properties on the <code>BoundedRangeModel</code>.
746 *
747 * @return the visibility
748 * @see BoundedRangeModel
749 */
750 public BoundedRangeModel getHorizontalVisibility() {
751 return visibility;
752 }
753
754 /**
755 * Gets the scroll offset, in pixels.
756 *
757 * @return the offset >= 0
758 */
759 public int getScrollOffset() {
760 return visibility.getValue();
761 }
762
763 /**
764 * Sets the scroll offset, in pixels.
765 *
766 * @param scrollOffset the offset >= 0
767 */
768 public void setScrollOffset(int scrollOffset) {
769 visibility.setValue(scrollOffset);
770 }
771
772 /**
773 * Scrolls the field left or right.
774 *
775 * @param r the region to scroll
776 */
777 public void scrollRectToVisible(Rectangle r) {
778 // convert to coordinate system of the bounded range
779 Insets i = getInsets();
780 int x0 = r.x + visibility.getValue() - i.left;
781 int x1 = x0 + r.width;
782 if (x0 < visibility.getValue()) {
783 // Scroll to the left
784 visibility.setValue(x0);
785 } else if (x1 > visibility.getValue() + visibility.getExtent()) {
786 // Scroll to the right
787 visibility.setValue(x1 - visibility.getExtent());
788 }
789 }
790
791 /**
792 * Returns true if the receiver has an <code>ActionListener</code>
793 * installed.
794 */
795 boolean hasActionListener() {
796 // Guaranteed to return a non-null array
797 Object[] listeners = listenerList.getListenerList();
798 // Process the listeners last to first, notifying
799 // those that are interested in this event
800 for (int i = listeners.length - 2; i >= 0; i -= 2) {
801 if (listeners[i] == ActionListener.class) {
802 return true;
803 }
804 }
805 return false;
806 }
807
808 // --- variables -------------------------------------------
809
810 /**
811 * Name of the action to send notification that the
812 * contents of the field have been accepted. Typically
813 * this is bound to a carriage-return.
814 */
815 public static final String notifyAction = "notify-field-accept";
816
817 private BoundedRangeModel visibility;
818 private int horizontalAlignment = LEADING;
819 private int columns;
820 private int columnWidth;
821 private String command;
822
823 private static final Action[] defaultActions = { new NotifyAction() };
824
825 /**
826 * @see #getUIClassID
827 * @see #readObject
828 */
829 private static final String uiClassID = "TextFieldUI";
830
831 // --- Action implementations -----------------------------------
832
833 // Note that JFormattedTextField.CommitAction extends this
834 static class NotifyAction extends TextAction {
835
836 NotifyAction() {
837 super (notifyAction);
838 }
839
840 public void actionPerformed(ActionEvent e) {
841 JTextComponent target = getFocusedComponent();
842 if (target instanceof JTextField) {
843 JTextField field = (JTextField) target;
844 field.postActionEvent();
845 }
846 }
847
848 public boolean isEnabled() {
849 JTextComponent target = getFocusedComponent();
850 if (target instanceof JTextField) {
851 return ((JTextField) target).hasActionListener();
852 }
853 return false;
854 }
855 }
856
857 class ScrollRepainter implements ChangeListener, Serializable {
858
859 public void stateChanged(ChangeEvent e) {
860 repaint();
861 }
862
863 }
864
865 /**
866 * See <code>readObject</code> and <code>writeObject</code> in
867 * <code>JComponent</code> for more
868 * information about serialization in Swing.
869 */
870 private void writeObject(ObjectOutputStream s) throws IOException {
871 s.defaultWriteObject();
872 if (getUIClassID().equals(uiClassID)) {
873 byte count = JComponent.getWriteObjCounter(this );
874 JComponent.setWriteObjCounter(this , --count);
875 if (count == 0 && ui != null) {
876 ui.installUI(this );
877 }
878 }
879 }
880
881 /**
882 * Returns a string representation of this <code>JTextField</code>.
883 * This method is intended to be used only for debugging purposes,
884 * and the content and format of the returned string may vary between
885 * implementations. The returned string may be empty but may not
886 * be <code>null</code>.
887 *
888 * @return a string representation of this <code>JTextField</code>
889 */
890 protected String paramString() {
891 String horizontalAlignmentString;
892 if (horizontalAlignment == LEFT) {
893 horizontalAlignmentString = "LEFT";
894 } else if (horizontalAlignment == CENTER) {
895 horizontalAlignmentString = "CENTER";
896 } else if (horizontalAlignment == RIGHT) {
897 horizontalAlignmentString = "RIGHT";
898 } else if (horizontalAlignment == LEADING) {
899 horizontalAlignmentString = "LEADING";
900 } else if (horizontalAlignment == TRAILING) {
901 horizontalAlignmentString = "TRAILING";
902 } else
903 horizontalAlignmentString = "";
904 String commandString = (command != null ? command : "");
905
906 return super .paramString() + ",columns=" + columns
907 + ",columnWidth=" + columnWidth + ",command="
908 + commandString + ",horizontalAlignment="
909 + horizontalAlignmentString;
910 }
911
912 /////////////////
913 // Accessibility support
914 ////////////////
915
916 /**
917 * Gets the <code>AccessibleContext</code> associated with this
918 * <code>JTextField</code>. For <code>JTextFields</code>,
919 * the <code>AccessibleContext</code> takes the form of an
920 * <code>AccessibleJTextField</code>.
921 * A new <code>AccessibleJTextField</code> instance is created
922 * if necessary.
923 *
924 * @return an <code>AccessibleJTextField</code> that serves as the
925 * <code>AccessibleContext</code> of this <code>JTextField</code>
926 */
927 public AccessibleContext getAccessibleContext() {
928 if (accessibleContext == null) {
929 accessibleContext = new AccessibleJTextField();
930 }
931 return accessibleContext;
932 }
933
934 /**
935 * This class implements accessibility support for the
936 * <code>JTextField</code> class. It provides an implementation of the
937 * Java Accessibility API appropriate to text field user-interface
938 * elements.
939 * <p>
940 * <strong>Warning:</strong>
941 * Serialized objects of this class will not be compatible with
942 * future Swing releases. The current serialization support is
943 * appropriate for short term storage or RMI between applications running
944 * the same version of Swing. As of 1.4, support for long term storage
945 * of all JavaBeans<sup><font size="-2">TM</font></sup>
946 * has been added to the <code>java.beans</code> package.
947 * Please see {@link java.beans.XMLEncoder}.
948 */
949 protected class AccessibleJTextField extends
950 AccessibleJTextComponent {
951
952 /**
953 * Gets the state set of this object.
954 *
955 * @return an instance of AccessibleStateSet describing the states
956 * of the object
957 * @see AccessibleState
958 */
959 public AccessibleStateSet getAccessibleStateSet() {
960 AccessibleStateSet states = super.getAccessibleStateSet();
961 states.add(AccessibleState.SINGLE_LINE);
962 return states;
963 }
964 }
965 }
|