--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/awt/TextComponent.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1231 @@
+/*
+ * Copyright 1995-2006 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 java.awt;
+
+import java.awt.peer.TextComponentPeer;
+import java.awt.event.*;
+import java.util.EventListener;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.IOException;
+import sun.awt.InputMethodSupport;
+import java.text.BreakIterator;
+import javax.swing.text.AttributeSet;
+import javax.accessibility.*;
+import java.awt.im.InputMethodRequests;
+
+
+/**
+ * The <code>TextComponent</code> class is the superclass of
+ * any component that allows the editing of some text.
+ * <p>
+ * A text component embodies a string of text. The
+ * <code>TextComponent</code> class defines a set of methods
+ * that determine whether or not this text is editable. If the
+ * component is editable, it defines another set of methods
+ * that supports a text insertion caret.
+ * <p>
+ * In addition, the class defines methods that are used
+ * to maintain a current <em>selection</em> from the text.
+ * The text selection, a substring of the component's text,
+ * is the target of editing operations. It is also referred
+ * to as the <em>selected text</em>.
+ *
+ * @author Sami Shaio
+ * @author Arthur van Hoff
+ * @since JDK1.0
+ */
+public class TextComponent extends Component implements Accessible {
+
+ /**
+ * The value of the text.
+ * A <code>null</code> value is the same as "".
+ *
+ * @serial
+ * @see #setText(String)
+ * @see #getText()
+ */
+ String text;
+
+ /**
+ * A boolean indicating whether or not this
+ * <code>TextComponent</code> is editable.
+ * It will be <code>true</code> if the text component
+ * is editable and <code>false</code> if not.
+ *
+ * @serial
+ * @see #isEditable()
+ */
+ boolean editable = true;
+
+ /**
+ * The selection refers to the selected text, and the
+ * <code>selectionStart</code> is the start position
+ * of the selected text.
+ *
+ * @serial
+ * @see #getSelectionStart()
+ * @see #setSelectionStart(int)
+ */
+ int selectionStart;
+
+ /**
+ * The selection refers to the selected text, and the
+ * <code>selectionEnd</code>
+ * is the end position of the selected text.
+ *
+ * @serial
+ * @see #getSelectionEnd()
+ * @see #setSelectionEnd(int)
+ */
+ int selectionEnd;
+
+ // A flag used to tell whether the background has been set by
+ // developer code (as opposed to AWT code). Used to determine
+ // the background color of non-editable TextComponents.
+ boolean backgroundSetByClientCode = false;
+
+ /**
+ * True if this <code>TextComponent</code> has access
+ * to the System clipboard.
+ */
+ transient private boolean canAccessClipboard;
+
+ transient protected TextListener textListener;
+
+ /*
+ * JDK 1.1 serialVersionUID
+ */
+ private static final long serialVersionUID = -2214773872412987419L;
+
+ /**
+ * Constructs a new text component initialized with the
+ * specified text. Sets the value of the cursor to
+ * <code>Cursor.TEXT_CURSOR</code>.
+ * @param text the text to be displayed; if
+ * <code>text</code> is <code>null</code>, the empty
+ * string <code>""</code> will be displayed
+ * @exception HeadlessException if
+ * <code>GraphicsEnvironment.isHeadless</code>
+ * returns true
+ * @see java.awt.GraphicsEnvironment#isHeadless
+ * @see java.awt.Cursor
+ */
+ TextComponent(String text) throws HeadlessException {
+ GraphicsEnvironment.checkHeadless();
+ this.text = (text != null) ? text : "";
+ setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+ checkSystemClipboardAccess();
+ }
+
+ private void enableInputMethodsIfNecessary() {
+ if (checkForEnableIM) {
+ checkForEnableIM = false;
+ try {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ boolean shouldEnable = false;
+ if (toolkit instanceof InputMethodSupport) {
+ shouldEnable = ((InputMethodSupport)toolkit)
+ .enableInputMethodsForTextComponent();
+ }
+ enableInputMethods(shouldEnable);
+ } catch (Exception e) {
+ // if something bad happens, just don't enable input methods
+ }
+ }
+ }
+
+ /**
+ * Enables or disables input method support for this text component. If input
+ * method support is enabled and the text component also processes key events,
+ * incoming events are offered to the current input method and will only be
+ * processed by the component or dispatched to its listeners if the input method
+ * does not consume them. Whether and how input method support for this text
+ * component is enabled or disabled by default is implementation dependent.
+ *
+ * @param enable true to enable, false to disable
+ * @see #processKeyEvent
+ * @since 1.2
+ */
+ public void enableInputMethods(boolean enable) {
+ checkForEnableIM = false;
+ super.enableInputMethods(enable);
+ }
+
+ boolean areInputMethodsEnabled() {
+ // moved from the constructor above to here and addNotify below,
+ // this call will initialize the toolkit if not already initialized.
+ if (checkForEnableIM) {
+ enableInputMethodsIfNecessary();
+ }
+
+ // TextComponent handles key events without touching the eventMask or
+ // having a key listener, so just check whether the flag is set
+ return (eventMask & AWTEvent.INPUT_METHODS_ENABLED_MASK) != 0;
+ }
+
+ public InputMethodRequests getInputMethodRequests() {
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) return peer.getInputMethodRequests();
+ else return null;
+ }
+
+
+
+ /**
+ * Makes this Component displayable by connecting it to a
+ * native screen resource.
+ * This method is called internally by the toolkit and should
+ * not be called directly by programs.
+ * @see java.awt.TextComponent#removeNotify
+ */
+ public void addNotify() {
+ super.addNotify();
+ enableInputMethodsIfNecessary();
+ }
+
+ /**
+ * Removes the <code>TextComponent</code>'s peer.
+ * The peer allows us to modify the appearance of the
+ * <code>TextComponent</code> without changing its
+ * functionality.
+ */
+ public void removeNotify() {
+ synchronized (getTreeLock()) {
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ text = peer.getText();
+ selectionStart = peer.getSelectionStart();
+ selectionEnd = peer.getSelectionEnd();
+ }
+ super.removeNotify();
+ }
+ }
+
+ /**
+ * Sets the text that is presented by this
+ * text component to be the specified text.
+ * @param t the new text;
+ * if this parameter is <code>null</code> then
+ * the text is set to the empty string ""
+ * @see java.awt.TextComponent#getText
+ */
+ public synchronized void setText(String t) {
+ text = (t != null) ? t : "";
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ peer.setText(text);
+ }
+ }
+
+ /**
+ * Returns the text that is presented by this text component.
+ * By default, this is an empty string.
+ *
+ * @return the value of this <code>TextComponent</code>
+ * @see java.awt.TextComponent#setText
+ */
+ public synchronized String getText() {
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ text = peer.getText();
+ }
+ return text;
+ }
+
+ /**
+ * Returns the selected text from the text that is
+ * presented by this text component.
+ * @return the selected text of this text component
+ * @see java.awt.TextComponent#select
+ */
+ public synchronized String getSelectedText() {
+ return getText().substring(getSelectionStart(), getSelectionEnd());
+ }
+
+ /**
+ * Indicates whether or not this text component is editable.
+ * @return <code>true</code> if this text component is
+ * editable; <code>false</code> otherwise.
+ * @see java.awt.TextComponent#setEditable
+ * @since JDK1.0
+ */
+ public boolean isEditable() {
+ return editable;
+ }
+
+ /**
+ * Sets the flag that determines whether or not this
+ * text component is editable.
+ * <p>
+ * If the flag is set to <code>true</code>, this text component
+ * becomes user editable. If the flag is set to <code>false</code>,
+ * the user cannot change the text of this text component.
+ * By default, non-editable text components have a background color
+ * of SystemColor.control. This default can be overridden by
+ * calling setBackground.
+ *
+ * @param b a flag indicating whether this text component
+ * is user editable.
+ * @see java.awt.TextComponent#isEditable
+ * @since JDK1.0
+ */
+ public synchronized void setEditable(boolean b) {
+ if (editable == b) {
+ return;
+ }
+
+ editable = b;
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ peer.setEditable(b);
+ }
+ }
+
+ /**
+ * Gets the background color of this text component.
+ *
+ * By default, non-editable text components have a background color
+ * of SystemColor.control. This default can be overridden by
+ * calling setBackground.
+ *
+ * @return This text component's background color.
+ * If this text component does not have a background color,
+ * the background color of its parent is returned.
+ * @see #setBackground(Color)
+ * @since JDK1.0
+ */
+ public Color getBackground() {
+ if (!editable && !backgroundSetByClientCode) {
+ return SystemColor.control;
+ }
+
+ return super.getBackground();
+ }
+
+ /**
+ * Sets the background color of this text component.
+ *
+ * @param c The color to become this text component's color.
+ * If this parameter is null then this text component
+ * will inherit the background color of its parent.
+ * @see #getBackground()
+ * @since JDK1.0
+ */
+ public void setBackground(Color c) {
+ backgroundSetByClientCode = true;
+ super.setBackground(c);
+ }
+
+ /**
+ * Gets the start position of the selected text in
+ * this text component.
+ * @return the start position of the selected text
+ * @see java.awt.TextComponent#setSelectionStart
+ * @see java.awt.TextComponent#getSelectionEnd
+ */
+ public synchronized int getSelectionStart() {
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ selectionStart = peer.getSelectionStart();
+ }
+ return selectionStart;
+ }
+
+ /**
+ * Sets the selection start for this text component to
+ * the specified position. The new start point is constrained
+ * to be at or before the current selection end. It also
+ * cannot be set to less than zero, the beginning of the
+ * component's text.
+ * If the caller supplies a value for <code>selectionStart</code>
+ * that is out of bounds, the method enforces these constraints
+ * silently, and without failure.
+ * @param selectionStart the start position of the
+ * selected text
+ * @see java.awt.TextComponent#getSelectionStart
+ * @see java.awt.TextComponent#setSelectionEnd
+ * @since JDK1.1
+ */
+ public synchronized void setSelectionStart(int selectionStart) {
+ /* Route through select method to enforce consistent policy
+ * between selectionStart and selectionEnd.
+ */
+ select(selectionStart, getSelectionEnd());
+ }
+
+ /**
+ * Gets the end position of the selected text in
+ * this text component.
+ * @return the end position of the selected text
+ * @see java.awt.TextComponent#setSelectionEnd
+ * @see java.awt.TextComponent#getSelectionStart
+ */
+ public synchronized int getSelectionEnd() {
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ selectionEnd = peer.getSelectionEnd();
+ }
+ return selectionEnd;
+ }
+
+ /**
+ * Sets the selection end for this text component to
+ * the specified position. The new end point is constrained
+ * to be at or after the current selection start. It also
+ * cannot be set beyond the end of the component's text.
+ * If the caller supplies a value for <code>selectionEnd</code>
+ * that is out of bounds, the method enforces these constraints
+ * silently, and without failure.
+ * @param selectionEnd the end position of the
+ * selected text
+ * @see java.awt.TextComponent#getSelectionEnd
+ * @see java.awt.TextComponent#setSelectionStart
+ * @since JDK1.1
+ */
+ public synchronized void setSelectionEnd(int selectionEnd) {
+ /* Route through select method to enforce consistent policy
+ * between selectionStart and selectionEnd.
+ */
+ select(getSelectionStart(), selectionEnd);
+ }
+
+ /**
+ * Selects the text between the specified start and end positions.
+ * <p>
+ * This method sets the start and end positions of the
+ * selected text, enforcing the restriction that the start position
+ * must be greater than or equal to zero. The end position must be
+ * greater than or equal to the start position, and less than or
+ * equal to the length of the text component's text. The
+ * character positions are indexed starting with zero.
+ * The length of the selection is
+ * <code>endPosition</code> - <code>startPosition</code>, so the
+ * character at <code>endPosition</code> is not selected.
+ * If the start and end positions of the selected text are equal,
+ * all text is deselected.
+ * <p>
+ * If the caller supplies values that are inconsistent or out of
+ * bounds, the method enforces these constraints silently, and
+ * without failure. Specifically, if the start position or end
+ * position is greater than the length of the text, it is reset to
+ * equal the text length. If the start position is less than zero,
+ * it is reset to zero, and if the end position is less than the
+ * start position, it is reset to the start position.
+ *
+ * @param selectionStart the zero-based index of the first
+ character (<code>char</code> value) to be selected
+ * @param selectionEnd the zero-based end position of the
+ text to be selected; the character (<code>char</code> value) at
+ <code>selectionEnd</code> is not selected
+ * @see java.awt.TextComponent#setSelectionStart
+ * @see java.awt.TextComponent#setSelectionEnd
+ * @see java.awt.TextComponent#selectAll
+ */
+ public synchronized void select(int selectionStart, int selectionEnd) {
+ String text = getText();
+ if (selectionStart < 0) {
+ selectionStart = 0;
+ }
+ if (selectionStart > text.length()) {
+ selectionStart = text.length();
+ }
+ if (selectionEnd > text.length()) {
+ selectionEnd = text.length();
+ }
+ if (selectionEnd < selectionStart) {
+ selectionEnd = selectionStart;
+ }
+
+ this.selectionStart = selectionStart;
+ this.selectionEnd = selectionEnd;
+
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ peer.select(selectionStart, selectionEnd);
+ }
+ }
+
+ /**
+ * Selects all the text in this text component.
+ * @see java.awt.TextComponent#select
+ */
+ public synchronized void selectAll() {
+ this.selectionStart = 0;
+ this.selectionEnd = getText().length();
+
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ peer.select(selectionStart, selectionEnd);
+ }
+ }
+
+ /**
+ * Sets the position of the text insertion caret.
+ * The caret position is constrained to be between 0
+ * and the last character of the text, inclusive.
+ * If the passed-in value is greater than this range,
+ * the value is set to the last character (or 0 if
+ * the <code>TextComponent</code> contains no text)
+ * and no error is returned. If the passed-in value is
+ * less than 0, an <code>IllegalArgumentException</code>
+ * is thrown.
+ *
+ * @param position the position of the text insertion caret
+ * @exception IllegalArgumentException if <code>position</code>
+ * is less than zero
+ * @since JDK1.1
+ */
+ public synchronized void setCaretPosition(int position) {
+ if (position < 0) {
+ throw new IllegalArgumentException("position less than zero.");
+ }
+
+ int maxposition = getText().length();
+ if (position > maxposition) {
+ position = maxposition;
+ }
+
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ peer.setCaretPosition(position);
+ } else {
+ select(position, position);
+ }
+ }
+
+ /**
+ * Returns the position of the text insertion caret.
+ * The caret position is constrained to be between 0
+ * and the last character of the text, inclusive.
+ * If the text or caret have not been set, the default
+ * caret position is 0.
+ *
+ * @return the position of the text insertion caret
+ * @see #setCaretPosition(int)
+ * @since JDK1.1
+ */
+ public synchronized int getCaretPosition() {
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ int position = 0;
+
+ if (peer != null) {
+ position = peer.getCaretPosition();
+ } else {
+ position = selectionStart;
+ }
+ int maxposition = getText().length();
+ if (position > maxposition) {
+ position = maxposition;
+ }
+ return position;
+ }
+
+ /**
+ * Adds the specified text event listener to receive text events
+ * from this text component.
+ * If <code>l</code> is <code>null</code>, no exception is
+ * thrown and no action is performed.
+ * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
+ * >AWT Threading Issues</a> for details on AWT's threading model.
+ *
+ * @param l the text event listener
+ * @see #removeTextListener
+ * @see #getTextListeners
+ * @see java.awt.event.TextListener
+ */
+ public synchronized void addTextListener(TextListener l) {
+ if (l == null) {
+ return;
+ }
+ textListener = AWTEventMulticaster.add(textListener, l);
+ newEventsOnly = true;
+ }
+
+ /**
+ * Removes the specified text event listener so that it no longer
+ * receives text events from this text component
+ * If <code>l</code> is <code>null</code>, no exception is
+ * thrown and no action is performed.
+ * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
+ * >AWT Threading Issues</a> for details on AWT's threading model.
+ *
+ * @param l the text listener
+ * @see #addTextListener
+ * @see #getTextListeners
+ * @see java.awt.event.TextListener
+ * @since JDK1.1
+ */
+ public synchronized void removeTextListener(TextListener l) {
+ if (l == null) {
+ return;
+ }
+ textListener = AWTEventMulticaster.remove(textListener, l);
+ }
+
+ /**
+ * Returns an array of all the text listeners
+ * registered on this text component.
+ *
+ * @return all of this text component's <code>TextListener</code>s
+ * or an empty array if no text
+ * listeners are currently registered
+ *
+ *
+ * @see #addTextListener
+ * @see #removeTextListener
+ * @since 1.4
+ */
+ public synchronized TextListener[] getTextListeners() {
+ return (TextListener[])(getListeners(TextListener.class));
+ }
+
+ /**
+ * Returns an array of all the objects currently registered
+ * as <code><em>Foo</em>Listener</code>s
+ * upon this <code>TextComponent</code>.
+ * <code><em>Foo</em>Listener</code>s are registered using the
+ * <code>add<em>Foo</em>Listener</code> method.
+ *
+ * <p>
+ * You can specify the <code>listenerType</code> argument
+ * with a class literal, such as
+ * <code><em>Foo</em>Listener.class</code>.
+ * For example, you can query a
+ * <code>TextComponent</code> <code>t</code>
+ * for its text listeners with the following code:
+ *
+ * <pre>TextListener[] tls = (TextListener[])(t.getListeners(TextListener.class));</pre>
+ *
+ * If no such listeners exist, this method returns an empty array.
+ *
+ * @param listenerType the type of listeners requested; this parameter
+ * should specify an interface that descends from
+ * <code>java.util.EventListener</code>
+ * @return an array of all objects registered as
+ * <code><em>Foo</em>Listener</code>s on this text component,
+ * or an empty array if no such
+ * listeners have been added
+ * @exception ClassCastException if <code>listenerType</code>
+ * doesn't specify a class or interface that implements
+ * <code>java.util.EventListener</code>
+ *
+ * @see #getTextListeners
+ * @since 1.3
+ */
+ public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
+ EventListener l = null;
+ if (listenerType == TextListener.class) {
+ l = textListener;
+ } else {
+ return super.getListeners(listenerType);
+ }
+ return AWTEventMulticaster.getListeners(l, listenerType);
+ }
+
+ // REMIND: remove when filtering is done at lower level
+ boolean eventEnabled(AWTEvent e) {
+ if (e.id == TextEvent.TEXT_VALUE_CHANGED) {
+ if ((eventMask & AWTEvent.TEXT_EVENT_MASK) != 0 ||
+ textListener != null) {
+ return true;
+ }
+ return false;
+ }
+ return super.eventEnabled(e);
+ }
+
+ /**
+ * Processes events on this text component. If the event is a
+ * <code>TextEvent</code>, it invokes the <code>processTextEvent</code>
+ * method else it invokes its superclass's <code>processEvent</code>.
+ * <p>Note that if the event parameter is <code>null</code>
+ * the behavior is unspecified and may result in an
+ * exception.
+ *
+ * @param e the event
+ */
+ protected void processEvent(AWTEvent e) {
+ if (e instanceof TextEvent) {
+ processTextEvent((TextEvent)e);
+ return;
+ }
+ super.processEvent(e);
+ }
+
+ /**
+ * Processes text events occurring on this text component by
+ * dispatching them to any registered <code>TextListener</code> objects.
+ * <p>
+ * NOTE: This method will not be called unless text events
+ * are enabled for this component. This happens when one of the
+ * following occurs:
+ * <ul>
+ * <li>A <code>TextListener</code> object is registered
+ * via <code>addTextListener</code>
+ * <li>Text events are enabled via <code>enableEvents</code>
+ * </ul>
+ * <p>Note that if the event parameter is <code>null</code>
+ * the behavior is unspecified and may result in an
+ * exception.
+ *
+ * @param e the text event
+ * @see Component#enableEvents
+ */
+ protected void processTextEvent(TextEvent e) {
+ TextListener listener = textListener;
+ if (listener != null) {
+ int id = e.getID();
+ switch (id) {
+ case TextEvent.TEXT_VALUE_CHANGED:
+ listener.textValueChanged(e);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns a string representing the state of this
+ * <code>TextComponent</code>. 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
+ * <code>null</code>.
+ *
+ * @return the parameter string of this text component
+ */
+ protected String paramString() {
+ String str = super.paramString() + ",text=" + getText();
+ if (editable) {
+ str += ",editable";
+ }
+ return str + ",selection=" + getSelectionStart() + "-" + getSelectionEnd();
+ }
+
+ /**
+ * Assigns a valid value to the canAccessClipboard instance variable.
+ */
+ private void checkSystemClipboardAccess() {
+ canAccessClipboard = true;
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ try {
+ sm.checkSystemClipboardAccess();
+ }
+ catch (SecurityException e) {
+ canAccessClipboard = false;
+ }
+ }
+ }
+
+ /*
+ * Serialization support.
+ */
+ /**
+ * The textComponent SerializedDataVersion.
+ *
+ * @serial
+ */
+ private int textComponentSerializedDataVersion = 1;
+
+ /**
+ * Writes default serializable fields to stream. Writes
+ * a list of serializable TextListener(s) as optional data.
+ * The non-serializable TextListener(s) are detected and
+ * no attempt is made to serialize them.
+ *
+ * @serialData Null terminated sequence of zero or more pairs.
+ * A pair consists of a String and Object.
+ * The String indicates the type of object and
+ * is one of the following :
+ * textListenerK indicating and TextListener object.
+ *
+ * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
+ * @see java.awt.Component#textListenerK
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws IOException
+ {
+ // Serialization support. Since the value of the fields
+ // selectionStart, selectionEnd, and text aren't necessarily
+ // up to date, we sync them up with the peer before serializing.
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ if (peer != null) {
+ text = peer.getText();
+ selectionStart = peer.getSelectionStart();
+ selectionEnd = peer.getSelectionEnd();
+ }
+
+ s.defaultWriteObject();
+
+ AWTEventMulticaster.save(s, textListenerK, textListener);
+ s.writeObject(null);
+ }
+
+ /**
+ * Read the ObjectInputStream, and if it isn't null,
+ * add a listener to receive text events fired by the
+ * TextComponent. Unrecognized keys or values will be
+ * ignored.
+ *
+ * @exception HeadlessException if
+ * <code>GraphicsEnvironment.isHeadless()</code> returns
+ * <code>true</code>
+ * @see #removeTextListener
+ * @see #addTextListener
+ * @see java.awt.GraphicsEnvironment#isHeadless
+ */
+ private void readObject(ObjectInputStream s)
+ throws ClassNotFoundException, IOException, HeadlessException
+ {
+ GraphicsEnvironment.checkHeadless();
+ s.defaultReadObject();
+
+ // Make sure the state we just read in for text,
+ // selectionStart and selectionEnd has legal values
+ this.text = (text != null) ? text : "";
+ select(selectionStart, selectionEnd);
+
+ Object keyOrNull;
+ while(null != (keyOrNull = s.readObject())) {
+ String key = ((String)keyOrNull).intern();
+
+ if (textListenerK == key) {
+ addTextListener((TextListener)(s.readObject()));
+ } else {
+ // skip value for unrecognized key
+ s.readObject();
+ }
+ }
+ enableInputMethodsIfNecessary();
+ checkSystemClipboardAccess();
+ }
+
+
+/////////////////
+// Accessibility support
+////////////////
+
+
+ /**
+ *
+ */
+ int getIndexAtPoint(Point p) {
+ return -1;
+/* To be fully implemented in a future release
+ if (peer == null) {
+ return -1;
+ }
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ return peer.getIndexAtPoint(p.x, p.y);
+*/
+ }
+
+
+ /**
+ *
+ */
+ Rectangle getCharacterBounds(int i) {
+ return null;
+/* To be fully implemented in a future release
+ if (peer == null) {
+ return null;
+ }
+ TextComponentPeer peer = (TextComponentPeer)this.peer;
+ return peer.getCharacterBounds(i);
+*/
+ }
+
+
+ /**
+ * Gets the AccessibleContext associated with this TextComponent.
+ * For text components, the AccessibleContext takes the form of an
+ * AccessibleAWTTextComponent.
+ * A new AccessibleAWTTextComponent instance is created if necessary.
+ *
+ * @return an AccessibleAWTTextComponent that serves as the
+ * AccessibleContext of this TextComponent
+ * @since 1.3
+ */
+ public AccessibleContext getAccessibleContext() {
+ if (accessibleContext == null) {
+ accessibleContext = new AccessibleAWTTextComponent();
+ }
+ return accessibleContext;
+ }
+
+ /**
+ * This class implements accessibility support for the
+ * <code>TextComponent</code> class. It provides an implementation of the
+ * Java Accessibility API appropriate to text component user-interface
+ * elements.
+ * @since 1.3
+ */
+ protected class AccessibleAWTTextComponent extends AccessibleAWTComponent
+ implements AccessibleText, TextListener
+ {
+ /*
+ * JDK 1.3 serialVersionUID
+ */
+ private static final long serialVersionUID = 3631432373506317811L;
+
+ /**
+ * Constructs an AccessibleAWTTextComponent. Adds a listener to track
+ * caret change.
+ */
+ public AccessibleAWTTextComponent() {
+ TextComponent.this.addTextListener(this);
+ }
+
+ /**
+ * TextListener notification of a text value change.
+ */
+ public void textValueChanged(TextEvent textEvent) {
+ Integer cpos = Integer.valueOf(TextComponent.this.getCaretPosition());
+ firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, cpos);
+ }
+
+ /**
+ * Gets the state set of the TextComponent.
+ * The AccessibleStateSet of an object is composed of a set of
+ * unique AccessibleStates. A change in the AccessibleStateSet
+ * of an object will cause a PropertyChangeEvent to be fired
+ * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
+ *
+ * @return an instance of AccessibleStateSet containing the
+ * current state set of the object
+ * @see AccessibleStateSet
+ * @see AccessibleState
+ * @see #addPropertyChangeListener
+ */
+ public AccessibleStateSet getAccessibleStateSet() {
+ AccessibleStateSet states = super.getAccessibleStateSet();
+ if (TextComponent.this.isEditable()) {
+ states.add(AccessibleState.EDITABLE);
+ }
+ return states;
+ }
+
+
+ /**
+ * Gets the role of this object.
+ *
+ * @return an instance of AccessibleRole describing the role of the
+ * object (AccessibleRole.TEXT)
+ * @see AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole() {
+ return AccessibleRole.TEXT;
+ }
+
+ /**
+ * Get the AccessibleText associated with this object. In the
+ * implementation of the Java Accessibility API for this class,
+ * return this object, which is responsible for implementing the
+ * AccessibleText interface on behalf of itself.
+ *
+ * @return this object
+ */
+ public AccessibleText getAccessibleText() {
+ return this;
+ }
+
+
+ // --- interface AccessibleText methods ------------------------
+
+ /**
+ * Many of these methods are just convenience methods; they
+ * just call the equivalent on the parent
+ */
+
+ /**
+ * Given a point in local coordinates, return the zero-based index
+ * of the character under that Point. If the point is invalid,
+ * this method returns -1.
+ *
+ * @param p the Point in local coordinates
+ * @return the zero-based index of the character under Point p.
+ */
+ public int getIndexAtPoint(Point p) {
+ return TextComponent.this.getIndexAtPoint(p);
+ }
+
+ /**
+ * Determines the bounding box of the character at the given
+ * index into the string. The bounds are returned in local
+ * coordinates. If the index is invalid a null rectangle
+ * is returned.
+ *
+ * @param i the index into the String >= 0
+ * @return the screen coordinates of the character's bounding box
+ */
+ public Rectangle getCharacterBounds(int i) {
+ return TextComponent.this.getCharacterBounds(i);
+ }
+
+ /**
+ * Returns the number of characters (valid indicies)
+ *
+ * @return the number of characters >= 0
+ */
+ public int getCharCount() {
+ return TextComponent.this.getText().length();
+ }
+
+ /**
+ * Returns the zero-based offset of the caret.
+ *
+ * Note: The character to the right of the caret will have the
+ * same index value as the offset (the caret is between
+ * two characters).
+ *
+ * @return the zero-based offset of the caret.
+ */
+ public int getCaretPosition() {
+ return TextComponent.this.getCaretPosition();
+ }
+
+ /**
+ * Returns the AttributeSet for a given character (at a given index).
+ *
+ * @param i the zero-based index into the text
+ * @return the AttributeSet of the character
+ */
+ public AttributeSet getCharacterAttribute(int i) {
+ return null; // No attributes in TextComponent
+ }
+
+ /**
+ * Returns the start offset within the selected text.
+ * If there is no selection, but there is
+ * a caret, the start and end offsets will be the same.
+ * Return 0 if the text is empty, or the caret position
+ * if no selection.
+ *
+ * @return the index into the text of the start of the selection >= 0
+ */
+ public int getSelectionStart() {
+ return TextComponent.this.getSelectionStart();
+ }
+
+ /**
+ * Returns the end offset within the selected text.
+ * If there is no selection, but there is
+ * a caret, the start and end offsets will be the same.
+ * Return 0 if the text is empty, or the caret position
+ * if no selection.
+ *
+ * @return the index into teh text of the end of the selection >= 0
+ */
+ public int getSelectionEnd() {
+ return TextComponent.this.getSelectionEnd();
+ }
+
+ /**
+ * Returns the portion of the text that is selected.
+ *
+ * @return the text, null if no selection
+ */
+ public String getSelectedText() {
+ String selText = TextComponent.this.getSelectedText();
+ // Fix for 4256662
+ if (selText == null || selText.equals("")) {
+ return null;
+ }
+ return selText;
+ }
+
+ /**
+ * Returns the String at a given index.
+ *
+ * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
+ * or AccessibleText.SENTENCE to retrieve
+ * @param index an index within the text >= 0
+ * @return the letter, word, or sentence,
+ * null for an invalid index or part
+ */
+ public String getAtIndex(int part, int index) {
+ if (index < 0 || index >= TextComponent.this.getText().length()) {
+ return null;
+ }
+ switch (part) {
+ case AccessibleText.CHARACTER:
+ return TextComponent.this.getText().substring(index, index+1);
+ case AccessibleText.WORD: {
+ String s = TextComponent.this.getText();
+ BreakIterator words = BreakIterator.getWordInstance();
+ words.setText(s);
+ int end = words.following(index);
+ return s.substring(words.previous(), end);
+ }
+ case AccessibleText.SENTENCE: {
+ String s = TextComponent.this.getText();
+ BreakIterator sentence = BreakIterator.getSentenceInstance();
+ sentence.setText(s);
+ int end = sentence.following(index);
+ return s.substring(sentence.previous(), end);
+ }
+ default:
+ return null;
+ }
+ }
+
+ private static final boolean NEXT = true;
+ private static final boolean PREVIOUS = false;
+
+ /**
+ * Needed to unify forward and backward searching.
+ * The method assumes that s is the text assigned to words.
+ */
+ private int findWordLimit(int index, BreakIterator words, boolean direction,
+ String s) {
+ // Fix for 4256660 and 4256661.
+ // Words iterator is different from character and sentence iterators
+ // in that end of one word is not necessarily start of another word.
+ // Please see java.text.BreakIterator JavaDoc. The code below is
+ // based on nextWordStartAfter example from BreakIterator.java.
+ int last = (direction == NEXT) ? words.following(index)
+ : words.preceding(index);
+ int current = (direction == NEXT) ? words.next()
+ : words.previous();
+ while (current != BreakIterator.DONE) {
+ for (int p = Math.min(last, current); p < Math.max(last, current); p++) {
+ if (Character.isLetter(s.charAt(p))) {
+ return last;
+ }
+ }
+ last = current;
+ current = (direction == NEXT) ? words.next()
+ : words.previous();
+ }
+ return BreakIterator.DONE;
+ }
+
+ /**
+ * Returns the String after a given index.
+ *
+ * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
+ * or AccessibleText.SENTENCE to retrieve
+ * @param index an index within the text >= 0
+ * @return the letter, word, or sentence, null for an invalid
+ * index or part
+ */
+ public String getAfterIndex(int part, int index) {
+ if (index < 0 || index >= TextComponent.this.getText().length()) {
+ return null;
+ }
+ switch (part) {
+ case AccessibleText.CHARACTER:
+ if (index+1 >= TextComponent.this.getText().length()) {
+ return null;
+ }
+ return TextComponent.this.getText().substring(index+1, index+2);
+ case AccessibleText.WORD: {
+ String s = TextComponent.this.getText();
+ BreakIterator words = BreakIterator.getWordInstance();
+ words.setText(s);
+ int start = findWordLimit(index, words, NEXT, s);
+ if (start == BreakIterator.DONE || start >= s.length()) {
+ return null;
+ }
+ int end = words.following(start);
+ if (end == BreakIterator.DONE || end >= s.length()) {
+ return null;
+ }
+ return s.substring(start, end);
+ }
+ case AccessibleText.SENTENCE: {
+ String s = TextComponent.this.getText();
+ BreakIterator sentence = BreakIterator.getSentenceInstance();
+ sentence.setText(s);
+ int start = sentence.following(index);
+ if (start == BreakIterator.DONE || start >= s.length()) {
+ return null;
+ }
+ int end = sentence.following(start);
+ if (end == BreakIterator.DONE || end >= s.length()) {
+ return null;
+ }
+ return s.substring(start, end);
+ }
+ default:
+ return null;
+ }
+ }
+
+
+ /**
+ * Returns the String before a given index.
+ *
+ * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
+ * or AccessibleText.SENTENCE to retrieve
+ * @param index an index within the text >= 0
+ * @return the letter, word, or sentence, null for an invalid index
+ * or part
+ */
+ public String getBeforeIndex(int part, int index) {
+ if (index < 0 || index > TextComponent.this.getText().length()-1) {
+ return null;
+ }
+ switch (part) {
+ case AccessibleText.CHARACTER:
+ if (index == 0) {
+ return null;
+ }
+ return TextComponent.this.getText().substring(index-1, index);
+ case AccessibleText.WORD: {
+ String s = TextComponent.this.getText();
+ BreakIterator words = BreakIterator.getWordInstance();
+ words.setText(s);
+ int end = findWordLimit(index, words, PREVIOUS, s);
+ if (end == BreakIterator.DONE) {
+ return null;
+ }
+ int start = words.preceding(end);
+ if (start == BreakIterator.DONE) {
+ return null;
+ }
+ return s.substring(start, end);
+ }
+ case AccessibleText.SENTENCE: {
+ String s = TextComponent.this.getText();
+ BreakIterator sentence = BreakIterator.getSentenceInstance();
+ sentence.setText(s);
+ int end = sentence.following(index);
+ end = sentence.previous();
+ int start = sentence.previous();
+ if (start == BreakIterator.DONE) {
+ return null;
+ }
+ return s.substring(start, end);
+ }
+ default:
+ return null;
+ }
+ }
+ } // end of AccessibleAWTTextComponent
+
+ private boolean checkForEnableIM = true;
+}