1 /*
2 * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package java.awt;
26
27 import java.awt.peer.TextComponentPeer;
28 import java.awt.event;
29 import java.util.EventListener;
30 import java.io.ObjectOutputStream;
31 import java.io.ObjectInputStream;
32 import java.io.IOException;
33 import sun.awt.InputMethodSupport;
34 import java.text.BreakIterator;
35 import javax.swing.text.AttributeSet;
36 import javax.accessibility;
37 import java.awt.im.InputMethodRequests;
38
39
40 /**
41 * The <code>TextComponent</code> class is the superclass of
42 * any component that allows the editing of some text.
43 * <p>
44 * A text component embodies a string of text. The
45 * <code>TextComponent</code> class defines a set of methods
46 * that determine whether or not this text is editable. If the
47 * component is editable, it defines another set of methods
48 * that supports a text insertion caret.
49 * <p>
50 * In addition, the class defines methods that are used
51 * to maintain a current <em>selection</em> from the text.
52 * The text selection, a substring of the component's text,
53 * is the target of editing operations. It is also referred
54 * to as the <em>selected text</em>.
55 *
56 * @author Sami Shaio
57 * @author Arthur van Hoff
58 * @since JDK1.0
59 */
60 public class TextComponent extends Component implements Accessible {
61
62 /**
63 * The value of the text.
64 * A <code>null</code> value is the same as "".
65 *
66 * @serial
67 * @see #setText(String)
68 * @see #getText()
69 */
70 String text;
71
72 /**
73 * A boolean indicating whether or not this
74 * <code>TextComponent</code> is editable.
75 * It will be <code>true</code> if the text component
76 * is editable and <code>false</code> if not.
77 *
78 * @serial
79 * @see #isEditable()
80 */
81 boolean editable = true;
82
83 /**
84 * The selection refers to the selected text, and the
85 * <code>selectionStart</code> is the start position
86 * of the selected text.
87 *
88 * @serial
89 * @see #getSelectionStart()
90 * @see #setSelectionStart(int)
91 */
92 int selectionStart;
93
94 /**
95 * The selection refers to the selected text, and the
96 * <code>selectionEnd</code>
97 * is the end position of the selected text.
98 *
99 * @serial
100 * @see #getSelectionEnd()
101 * @see #setSelectionEnd(int)
102 */
103 int selectionEnd;
104
105 // A flag used to tell whether the background has been set by
106 // developer code (as opposed to AWT code). Used to determine
107 // the background color of non-editable TextComponents.
108 boolean backgroundSetByClientCode = false;
109
110 /**
111 * True if this <code>TextComponent</code> has access
112 * to the System clipboard.
113 */
114 transient private boolean canAccessClipboard;
115
116 transient protected TextListener textListener;
117
118 /*
119 * JDK 1.1 serialVersionUID
120 */
121 private static final long serialVersionUID = -2214773872412987419L;
122
123 /**
124 * Constructs a new text component initialized with the
125 * specified text. Sets the value of the cursor to
126 * <code>Cursor.TEXT_CURSOR</code>.
127 * @param text the text to be displayed; if
128 * <code>text</code> is <code>null</code>, the empty
129 * string <code>""</code> will be displayed
130 * @exception HeadlessException if
131 * <code>GraphicsEnvironment.isHeadless</code>
132 * returns true
133 * @see java.awt.GraphicsEnvironment#isHeadless
134 * @see java.awt.Cursor
135 */
136 TextComponent(String text) throws HeadlessException {
137 GraphicsEnvironment.checkHeadless();
138 this.text = (text != null) ? text : "";
139 setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
140 checkSystemClipboardAccess();
141 }
142
143 private void enableInputMethodsIfNecessary() {
144 if (checkForEnableIM) {
145 checkForEnableIM = false;
146 try {
147 Toolkit toolkit = Toolkit.getDefaultToolkit();
148 boolean shouldEnable = false;
149 if (toolkit instanceof InputMethodSupport) {
150 shouldEnable = ((InputMethodSupport)toolkit)
151 .enableInputMethodsForTextComponent();
152 }
153 enableInputMethods(shouldEnable);
154 } catch (Exception e) {
155 // if something bad happens, just don't enable input methods
156 }
157 }
158 }
159
160 /**
161 * Enables or disables input method support for this text component. If input
162 * method support is enabled and the text component also processes key events,
163 * incoming events are offered to the current input method and will only be
164 * processed by the component or dispatched to its listeners if the input method
165 * does not consume them. Whether and how input method support for this text
166 * component is enabled or disabled by default is implementation dependent.
167 *
168 * @param enable true to enable, false to disable
169 * @see #processKeyEvent
170 * @since 1.2
171 */
172 public void enableInputMethods(boolean enable) {
173 checkForEnableIM = false;
174 super.enableInputMethods(enable);
175 }
176
177 boolean areInputMethodsEnabled() {
178 // moved from the constructor above to here and addNotify below,
179 // this call will initialize the toolkit if not already initialized.
180 if (checkForEnableIM) {
181 enableInputMethodsIfNecessary();
182 }
183
184 // TextComponent handles key events without touching the eventMask or
185 // having a key listener, so just check whether the flag is set
186 return (eventMask & AWTEvent.INPUT_METHODS_ENABLED_MASK) != 0;
187 }
188
189 public InputMethodRequests getInputMethodRequests() {
190 TextComponentPeer peer = (TextComponentPeer)this.peer;
191 if (peer != null) return peer.getInputMethodRequests();
192 else return null;
193 }
194
195
196
197 /**
198 * Makes this Component displayable by connecting it to a
199 * native screen resource.
200 * This method is called internally by the toolkit and should
201 * not be called directly by programs.
202 * @see java.awt.TextComponent#removeNotify
203 */
204 public void addNotify() {
205 super.addNotify();
206 enableInputMethodsIfNecessary();
207 }
208
209 /**
210 * Removes the <code>TextComponent</code>'s peer.
211 * The peer allows us to modify the appearance of the
212 * <code>TextComponent</code> without changing its
213 * functionality.
214 */
215 public void removeNotify() {
216 synchronized (getTreeLock()) {
217 TextComponentPeer peer = (TextComponentPeer)this.peer;
218 if (peer != null) {
219 text = peer.getText();
220 selectionStart = peer.getSelectionStart();
221 selectionEnd = peer.getSelectionEnd();
222 }
223 super.removeNotify();
224 }
225 }
226
227 /**
228 * Sets the text that is presented by this
229 * text component to be the specified text.
230 * @param t the new text;
231 * if this parameter is <code>null</code> then
232 * the text is set to the empty string ""
233 * @see java.awt.TextComponent#getText
234 */
235 public synchronized void setText(String t) {
236 text = (t != null) ? t : "";
237 TextComponentPeer peer = (TextComponentPeer)this.peer;
238 if (peer != null) {
239 peer.setText(text);
240 }
241 }
242
243 /**
244 * Returns the text that is presented by this text component.
245 * By default, this is an empty string.
246 *
247 * @return the value of this <code>TextComponent</code>
248 * @see java.awt.TextComponent#setText
249 */
250 public synchronized String getText() {
251 TextComponentPeer peer = (TextComponentPeer)this.peer;
252 if (peer != null) {
253 text = peer.getText();
254 }
255 return text;
256 }
257
258 /**
259 * Returns the selected text from the text that is
260 * presented by this text component.
261 * @return the selected text of this text component
262 * @see java.awt.TextComponent#select
263 */
264 public synchronized String getSelectedText() {
265 return getText().substring(getSelectionStart(), getSelectionEnd());
266 }
267
268 /**
269 * Indicates whether or not this text component is editable.
270 * @return <code>true</code> if this text component is
271 * editable; <code>false</code> otherwise.
272 * @see java.awt.TextComponent#setEditable
273 * @since JDK1.0
274 */
275 public boolean isEditable() {
276 return editable;
277 }
278
279 /**
280 * Sets the flag that determines whether or not this
281 * text component is editable.
282 * <p>
283 * If the flag is set to <code>true</code>, this text component
284 * becomes user editable. If the flag is set to <code>false</code>,
285 * the user cannot change the text of this text component.
286 * By default, non-editable text components have a background color
287 * of SystemColor.control. This default can be overridden by
288 * calling setBackground.
289 *
290 * @param b a flag indicating whether this text component
291 * is user editable.
292 * @see java.awt.TextComponent#isEditable
293 * @since JDK1.0
294 */
295 public synchronized void setEditable(boolean b) {
296 if (editable == b) {
297 return;
298 }
299
300 editable = b;
301 TextComponentPeer peer = (TextComponentPeer)this.peer;
302 if (peer != null) {
303 peer.setEditable(b);
304 }
305 }
306
307 /**
308 * Gets the background color of this text component.
309 *
310 * By default, non-editable text components have a background color
311 * of SystemColor.control. This default can be overridden by
312 * calling setBackground.
313 *
314 * @return This text component's background color.
315 * If this text component does not have a background color,
316 * the background color of its parent is returned.
317 * @see #setBackground(Color)
318 * @since JDK1.0
319 */
320 public Color getBackground() {
321 if (!editable && !backgroundSetByClientCode) {
322 return SystemColor.control;
323 }
324
325 return super.getBackground();
326 }
327
328 /**
329 * Sets the background color of this text component.
330 *
331 * @param c The color to become this text component's color.
332 * If this parameter is null then this text component
333 * will inherit the background color of its parent.
334 * @see #getBackground()
335 * @since JDK1.0
336 */
337 public void setBackground(Color c) {
338 backgroundSetByClientCode = true;
339 super.setBackground(c);
340 }
341
342 /**
343 * Gets the start position of the selected text in
344 * this text component.
345 * @return the start position of the selected text
346 * @see java.awt.TextComponent#setSelectionStart
347 * @see java.awt.TextComponent#getSelectionEnd
348 */
349 public synchronized int getSelectionStart() {
350 TextComponentPeer peer = (TextComponentPeer)this.peer;
351 if (peer != null) {
352 selectionStart = peer.getSelectionStart();
353 }
354 return selectionStart;
355 }
356
357 /**
358 * Sets the selection start for this text component to
359 * the specified position. The new start point is constrained
360 * to be at or before the current selection end. It also
361 * cannot be set to less than zero, the beginning of the
362 * component's text.
363 * If the caller supplies a value for <code>selectionStart</code>
364 * that is out of bounds, the method enforces these constraints
365 * silently, and without failure.
366 * @param selectionStart the start position of the
367 * selected text
368 * @see java.awt.TextComponent#getSelectionStart
369 * @see java.awt.TextComponent#setSelectionEnd
370 * @since JDK1.1
371 */
372 public synchronized void setSelectionStart(int selectionStart) {
373 /* Route through select method to enforce consistent policy
374 * between selectionStart and selectionEnd.
375 */
376 select(selectionStart, getSelectionEnd());
377 }
378
379 /**
380 * Gets the end position of the selected text in
381 * this text component.
382 * @return the end position of the selected text
383 * @see java.awt.TextComponent#setSelectionEnd
384 * @see java.awt.TextComponent#getSelectionStart
385 */
386 public synchronized int getSelectionEnd() {
387 TextComponentPeer peer = (TextComponentPeer)this.peer;
388 if (peer != null) {
389 selectionEnd = peer.getSelectionEnd();
390 }
391 return selectionEnd;
392 }
393
394 /**
395 * Sets the selection end for this text component to
396 * the specified position. The new end point is constrained
397 * to be at or after the current selection start. It also
398 * cannot be set beyond the end of the component's text.
399 * If the caller supplies a value for <code>selectionEnd</code>
400 * that is out of bounds, the method enforces these constraints
401 * silently, and without failure.
402 * @param selectionEnd the end position of the
403 * selected text
404 * @see java.awt.TextComponent#getSelectionEnd
405 * @see java.awt.TextComponent#setSelectionStart
406 * @since JDK1.1
407 */
408 public synchronized void setSelectionEnd(int selectionEnd) {
409 /* Route through select method to enforce consistent policy
410 * between selectionStart and selectionEnd.
411 */
412 select(getSelectionStart(), selectionEnd);
413 }
414
415 /**
416 * Selects the text between the specified start and end positions.
417 * <p>
418 * This method sets the start and end positions of the
419 * selected text, enforcing the restriction that the start position
420 * must be greater than or equal to zero. The end position must be
421 * greater than or equal to the start position, and less than or
422 * equal to the length of the text component's text. The
423 * character positions are indexed starting with zero.
424 * The length of the selection is
425 * <code>endPosition</code> - <code>startPosition</code>, so the
426 * character at <code>endPosition</code> is not selected.
427 * If the start and end positions of the selected text are equal,
428 * all text is deselected.
429 * <p>
430 * If the caller supplies values that are inconsistent or out of
431 * bounds, the method enforces these constraints silently, and
432 * without failure. Specifically, if the start position or end
433 * position is greater than the length of the text, it is reset to
434 * equal the text length. If the start position is less than zero,
435 * it is reset to zero, and if the end position is less than the
436 * start position, it is reset to the start position.
437 *
438 * @param selectionStart the zero-based index of the first
439 character (<code>char</code> value) to be selected
440 * @param selectionEnd the zero-based end position of the
441 text to be selected; the character (<code>char</code> value) at
442 <code>selectionEnd</code> is not selected
443 * @see java.awt.TextComponent#setSelectionStart
444 * @see java.awt.TextComponent#setSelectionEnd
445 * @see java.awt.TextComponent#selectAll
446 */
447 public synchronized void select(int selectionStart, int selectionEnd) {
448 String text = getText();
449 if (selectionStart < 0) {
450 selectionStart = 0;
451 }
452 if (selectionStart > text.length()) {
453 selectionStart = text.length();
454 }
455 if (selectionEnd > text.length()) {
456 selectionEnd = text.length();
457 }
458 if (selectionEnd < selectionStart) {
459 selectionEnd = selectionStart;
460 }
461
462 this.selectionStart = selectionStart;
463 this.selectionEnd = selectionEnd;
464
465 TextComponentPeer peer = (TextComponentPeer)this.peer;
466 if (peer != null) {
467 peer.select(selectionStart, selectionEnd);
468 }
469 }
470
471 /**
472 * Selects all the text in this text component.
473 * @see java.awt.TextComponent#select
474 */
475 public synchronized void selectAll() {
476 this.selectionStart = 0;
477 this.selectionEnd = getText().length();
478
479 TextComponentPeer peer = (TextComponentPeer)this.peer;
480 if (peer != null) {
481 peer.select(selectionStart, selectionEnd);
482 }
483 }
484
485 /**
486 * Sets the position of the text insertion caret.
487 * The caret position is constrained to be between 0
488 * and the last character of the text, inclusive.
489 * If the passed-in value is greater than this range,
490 * the value is set to the last character (or 0 if
491 * the <code>TextComponent</code> contains no text)
492 * and no error is returned. If the passed-in value is
493 * less than 0, an <code>IllegalArgumentException</code>
494 * is thrown.
495 *
496 * @param position the position of the text insertion caret
497 * @exception IllegalArgumentException if <code>position</code>
498 * is less than zero
499 * @since JDK1.1
500 */
501 public synchronized void setCaretPosition(int position) {
502 if (position < 0) {
503 throw new IllegalArgumentException("position less than zero.");
504 }
505
506 int maxposition = getText().length();
507 if (position > maxposition) {
508 position = maxposition;
509 }
510
511 TextComponentPeer peer = (TextComponentPeer)this.peer;
512 if (peer != null) {
513 peer.setCaretPosition(position);
514 } else {
515 select(position, position);
516 }
517 }
518
519 /**
520 * Returns the position of the text insertion caret.
521 * The caret position is constrained to be between 0
522 * and the last character of the text, inclusive.
523 * If the text or caret have not been set, the default
524 * caret position is 0.
525 *
526 * @return the position of the text insertion caret
527 * @see #setCaretPosition(int)
528 * @since JDK1.1
529 */
530 public synchronized int getCaretPosition() {
531 TextComponentPeer peer = (TextComponentPeer)this.peer;
532 int position = 0;
533
534 if (peer != null) {
535 position = peer.getCaretPosition();
536 } else {
537 position = selectionStart;
538 }
539 int maxposition = getText().length();
540 if (position > maxposition) {
541 position = maxposition;
542 }
543 return position;
544 }
545
546 /**
547 * Adds the specified text event listener to receive text events
548 * from this text component.
549 * If <code>l</code> is <code>null</code>, no exception is
550 * thrown and no action is performed.
551 * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
552 * >AWT Threading Issues</a> for details on AWT's threading model.
553 *
554 * @param l the text event listener
555 * @see #removeTextListener
556 * @see #getTextListeners
557 * @see java.awt.event.TextListener
558 */
559 public synchronized void addTextListener(TextListener l) {
560 if (l == null) {
561 return;
562 }
563 textListener = AWTEventMulticaster.add(textListener, l);
564 newEventsOnly = true;
565 }
566
567 /**
568 * Removes the specified text event listener so that it no longer
569 * receives text events from this text component
570 * If <code>l</code> is <code>null</code>, no exception is
571 * thrown and no action is performed.
572 * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
573 * >AWT Threading Issues</a> for details on AWT's threading model.
574 *
575 * @param l the text listener
576 * @see #addTextListener
577 * @see #getTextListeners
578 * @see java.awt.event.TextListener
579 * @since JDK1.1
580 */
581 public synchronized void removeTextListener(TextListener l) {
582 if (l == null) {
583 return;
584 }
585 textListener = AWTEventMulticaster.remove(textListener, l);
586 }
587
588 /**
589 * Returns an array of all the text listeners
590 * registered on this text component.
591 *
592 * @return all of this text component's <code>TextListener</code>s
593 * or an empty array if no text
594 * listeners are currently registered
595 *
596 *
597 * @see #addTextListener
598 * @see #removeTextListener
599 * @since 1.4
600 */
601 public synchronized TextListener[] getTextListeners() {
602 return (TextListener[])(getListeners(TextListener.class));
603 }
604
605 /**
606 * Returns an array of all the objects currently registered
607 * as <code><em>Foo</em>Listener</code>s
608 * upon this <code>TextComponent</code>.
609 * <code><em>Foo</em>Listener</code>s are registered using the
610 * <code>add<em>Foo</em>Listener</code> method.
611 *
612 * <p>
613 * You can specify the <code>listenerType</code> argument
614 * with a class literal, such as
615 * <code><em>Foo</em>Listener.class</code>.
616 * For example, you can query a
617 * <code>TextComponent</code> <code>t</code>
618 * for its text listeners with the following code:
619 *
620 * <pre>TextListener[] tls = (TextListener[])(t.getListeners(TextListener.class));</pre>
621 *
622 * If no such listeners exist, this method returns an empty array.
623 *
624 * @param listenerType the type of listeners requested; this parameter
625 * should specify an interface that descends from
626 * <code>java.util.EventListener</code>
627 * @return an array of all objects registered as
628 * <code><em>Foo</em>Listener</code>s on this text component,
629 * or an empty array if no such
630 * listeners have been added
631 * @exception ClassCastException if <code>listenerType</code>
632 * doesn't specify a class or interface that implements
633 * <code>java.util.EventListener</code>
634 *
635 * @see #getTextListeners
636 * @since 1.3
637 */
638 public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
639 EventListener l = null;
640 if (listenerType == TextListener.class) {
641 l = textListener;
642 } else {
643 return super.getListeners(listenerType);
644 }
645 return AWTEventMulticaster.getListeners(l, listenerType);
646 }
647
648 // REMIND: remove when filtering is done at lower level
649 boolean eventEnabled(AWTEvent e) {
650 if (e.id == TextEvent.TEXT_VALUE_CHANGED) {
651 if ((eventMask & AWTEvent.TEXT_EVENT_MASK) != 0 ||
652 textListener != null) {
653 return true;
654 }
655 return false;
656 }
657 return super.eventEnabled(e);
658 }
659
660 /**
661 * Processes events on this text component. If the event is a
662 * <code>TextEvent</code>, it invokes the <code>processTextEvent</code>
663 * method else it invokes its superclass's <code>processEvent</code>.
664 * <p>Note that if the event parameter is <code>null</code>
665 * the behavior is unspecified and may result in an
666 * exception.
667 *
668 * @param e the event
669 */
670 protected void processEvent(AWTEvent e) {
671 if (e instanceof TextEvent) {
672 processTextEvent((TextEvent)e);
673 return;
674 }
675 super.processEvent(e);
676 }
677
678 /**
679 * Processes text events occurring on this text component by
680 * dispatching them to any registered <code>TextListener</code> objects.
681 * <p>
682 * NOTE: This method will not be called unless text events
683 * are enabled for this component. This happens when one of the
684 * following occurs:
685 * <ul>
686 * <li>A <code>TextListener</code> object is registered
687 * via <code>addTextListener</code>
688 * <li>Text events are enabled via <code>enableEvents</code>
689 * </ul>
690 * <p>Note that if the event parameter is <code>null</code>
691 * the behavior is unspecified and may result in an
692 * exception.
693 *
694 * @param e the text event
695 * @see Component#enableEvents
696 */
697 protected void processTextEvent(TextEvent e) {
698 TextListener listener = textListener;
699 if (listener != null) {
700 int id = e.getID();
701 switch (id) {
702 case TextEvent.TEXT_VALUE_CHANGED:
703 listener.textValueChanged(e);
704 break;
705 }
706 }
707 }
708
709 /**
710 * Returns a string representing the state of this
711 * <code>TextComponent</code>. This
712 * method is intended to be used only for debugging purposes, and the
713 * content and format of the returned string may vary between
714 * implementations. The returned string may be empty but may not be
715 * <code>null</code>.
716 *
717 * @return the parameter string of this text component
718 */
719 protected String paramString() {
720 String str = super.paramString() + ",text=" + getText();
721 if (editable) {
722 str += ",editable";
723 }
724 return str + ",selection=" + getSelectionStart() + "-" + getSelectionEnd();
725 }
726
727 /**
728 * Assigns a valid value to the canAccessClipboard instance variable.
729 */
730 private void checkSystemClipboardAccess() {
731 canAccessClipboard = true;
732 SecurityManager sm = System.getSecurityManager();
733 if (sm != null) {
734 try {
735 sm.checkSystemClipboardAccess();
736 }
737 catch (SecurityException e) {
738 canAccessClipboard = false;
739 }
740 }
741 }
742
743 /*
744 * Serialization support.
745 */
746 /**
747 * The textComponent SerializedDataVersion.
748 *
749 * @serial
750 */
751 private int textComponentSerializedDataVersion = 1;
752
753 /**
754 * Writes default serializable fields to stream. Writes
755 * a list of serializable TextListener(s) as optional data.
756 * The non-serializable TextListener(s) are detected and
757 * no attempt is made to serialize them.
758 *
759 * @serialData Null terminated sequence of zero or more pairs.
760 * A pair consists of a String and Object.
761 * The String indicates the type of object and
762 * is one of the following :
763 * textListenerK indicating and TextListener object.
764 *
765 * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
766 * @see java.awt.Component#textListenerK
767 */
768 private void writeObject(java.io.ObjectOutputStream s)
769 throws IOException
770 {
771 // Serialization support. Since the value of the fields
772 // selectionStart, selectionEnd, and text aren't necessarily
773 // up to date, we sync them up with the peer before serializing.
774 TextComponentPeer peer = (TextComponentPeer)this.peer;
775 if (peer != null) {
776 text = peer.getText();
777 selectionStart = peer.getSelectionStart();
778 selectionEnd = peer.getSelectionEnd();
779 }
780
781 s.defaultWriteObject();
782
783 AWTEventMulticaster.save(s, textListenerK, textListener);
784 s.writeObject(null);
785 }
786
787 /**
788 * Read the ObjectInputStream, and if it isn't null,
789 * add a listener to receive text events fired by the
790 * TextComponent. Unrecognized keys or values will be
791 * ignored.
792 *
793 * @exception HeadlessException if
794 * <code>GraphicsEnvironment.isHeadless()</code> returns
795 * <code>true</code>
796 * @see #removeTextListener
797 * @see #addTextListener
798 * @see java.awt.GraphicsEnvironment#isHeadless
799 */
800 private void readObject(ObjectInputStream s)
801 throws ClassNotFoundException, IOException, HeadlessException
802 {
803 GraphicsEnvironment.checkHeadless();
804 s.defaultReadObject();
805
806 // Make sure the state we just read in for text,
807 // selectionStart and selectionEnd has legal values
808 this.text = (text != null) ? text : "";
809 select(selectionStart, selectionEnd);
810
811 Object keyOrNull;
812 while(null != (keyOrNull = s.readObject())) {
813 String key = ((String)keyOrNull).intern();
814
815 if (textListenerK == key) {
816 addTextListener((TextListener)(s.readObject()));
817 } else {
818 // skip value for unrecognized key
819 s.readObject();
820 }
821 }
822 enableInputMethodsIfNecessary();
823 checkSystemClipboardAccess();
824 }
825
826
827 /////////////////
828 // Accessibility support
829 ////////////////
830
831
832 /**
833 *
834 */
835 int getIndexAtPoint(Point p) {
836 return -1;
837 /* To be fully implemented in a future release
838 if (peer == null) {
839 return -1;
840 }
841 TextComponentPeer peer = (TextComponentPeer)this.peer;
842 return peer.getIndexAtPoint(p.x, p.y);
843 */
844 }
845
846
847 /**
848 *
849 */
850 Rectangle getCharacterBounds(int i) {
851 return null;
852 /* To be fully implemented in a future release
853 if (peer == null) {
854 return null;
855 }
856 TextComponentPeer peer = (TextComponentPeer)this.peer;
857 return peer.getCharacterBounds(i);
858 */
859 }
860
861
862 /**
863 * Gets the AccessibleContext associated with this TextComponent.
864 * For text components, the AccessibleContext takes the form of an
865 * AccessibleAWTTextComponent.
866 * A new AccessibleAWTTextComponent instance is created if necessary.
867 *
868 * @return an AccessibleAWTTextComponent that serves as the
869 * AccessibleContext of this TextComponent
870 * @since 1.3
871 */
872 public AccessibleContext getAccessibleContext() {
873 if (accessibleContext == null) {
874 accessibleContext = new AccessibleAWTTextComponent();
875 }
876 return accessibleContext;
877 }
878
879 /**
880 * This class implements accessibility support for the
881 * <code>TextComponent</code> class. It provides an implementation of the
882 * Java Accessibility API appropriate to text component user-interface
883 * elements.
884 * @since 1.3
885 */
886 protected class AccessibleAWTTextComponent extends AccessibleAWTComponent
887 implements AccessibleText, TextListener
888 {
889 /*
890 * JDK 1.3 serialVersionUID
891 */
892 private static final long serialVersionUID = 3631432373506317811L;
893
894 /**
895 * Constructs an AccessibleAWTTextComponent. Adds a listener to track
896 * caret change.
897 */
898 public AccessibleAWTTextComponent() {
899 TextComponent.this.addTextListener(this);
900 }
901
902 /**
903 * TextListener notification of a text value change.
904 */
905 public void textValueChanged(TextEvent textEvent) {
906 Integer cpos = Integer.valueOf(TextComponent.this.getCaretPosition());
907 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, cpos);
908 }
909
910 /**
911 * Gets the state set of the TextComponent.
912 * The AccessibleStateSet of an object is composed of a set of
913 * unique AccessibleStates. A change in the AccessibleStateSet
914 * of an object will cause a PropertyChangeEvent to be fired
915 * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
916 *
917 * @return an instance of AccessibleStateSet containing the
918 * current state set of the object
919 * @see AccessibleStateSet
920 * @see AccessibleState
921 * @see #addPropertyChangeListener
922 */
923 public AccessibleStateSet getAccessibleStateSet() {
924 AccessibleStateSet states = super.getAccessibleStateSet();
925 if (TextComponent.this.isEditable()) {
926 states.add(AccessibleState.EDITABLE);
927 }
928 return states;
929 }
930
931
932 /**
933 * Gets the role of this object.
934 *
935 * @return an instance of AccessibleRole describing the role of the
936 * object (AccessibleRole.TEXT)
937 * @see AccessibleRole
938 */
939 public AccessibleRole getAccessibleRole() {
940 return AccessibleRole.TEXT;
941 }
942
943 /**
944 * Get the AccessibleText associated with this object. In the
945 * implementation of the Java Accessibility API for this class,
946 * return this object, which is responsible for implementing the
947 * AccessibleText interface on behalf of itself.
948 *
949 * @return this object
950 */
951 public AccessibleText getAccessibleText() {
952 return this;
953 }
954
955
956 // --- interface AccessibleText methods ------------------------
957
958 /**
959 * Many of these methods are just convenience methods; they
960 * just call the equivalent on the parent
961 */
962
963 /**
964 * Given a point in local coordinates, return the zero-based index
965 * of the character under that Point. If the point is invalid,
966 * this method returns -1.
967 *
968 * @param p the Point in local coordinates
969 * @return the zero-based index of the character under Point p.
970 */
971 public int getIndexAtPoint(Point p) {
972 return TextComponent.this.getIndexAtPoint(p);
973 }
974
975 /**
976 * Determines the bounding box of the character at the given
977 * index into the string. The bounds are returned in local
978 * coordinates. If the index is invalid a null rectangle
979 * is returned.
980 *
981 * @param i the index into the String >= 0
982 * @return the screen coordinates of the character's bounding box
983 */
984 public Rectangle getCharacterBounds(int i) {
985 return TextComponent.this.getCharacterBounds(i);
986 }
987
988 /**
989 * Returns the number of characters (valid indicies)
990 *
991 * @return the number of characters >= 0
992 */
993 public int getCharCount() {
994 return TextComponent.this.getText().length();
995 }
996
997 /**
998 * Returns the zero-based offset of the caret.
999 *
1000 * Note: The character to the right of the caret will have the
1001 * same index value as the offset (the caret is between
1002 * two characters).
1003 *
1004 * @return the zero-based offset of the caret.
1005 */
1006 public int getCaretPosition() {
1007 return TextComponent.this.getCaretPosition();
1008 }
1009
1010 /**
1011 * Returns the AttributeSet for a given character (at a given index).
1012 *
1013 * @param i the zero-based index into the text
1014 * @return the AttributeSet of the character
1015 */
1016 public AttributeSet getCharacterAttribute(int i) {
1017 return null; // No attributes in TextComponent
1018 }
1019
1020 /**
1021 * Returns the start offset within the selected text.
1022 * If there is no selection, but there is
1023 * a caret, the start and end offsets will be the same.
1024 * Return 0 if the text is empty, or the caret position
1025 * if no selection.
1026 *
1027 * @return the index into the text of the start of the selection >= 0
1028 */
1029 public int getSelectionStart() {
1030 return TextComponent.this.getSelectionStart();
1031 }
1032
1033 /**
1034 * Returns the end offset within the selected text.
1035 * If there is no selection, but there is
1036 * a caret, the start and end offsets will be the same.
1037 * Return 0 if the text is empty, or the caret position
1038 * if no selection.
1039 *
1040 * @return the index into teh text of the end of the selection >= 0
1041 */
1042 public int getSelectionEnd() {
1043 return TextComponent.this.getSelectionEnd();
1044 }
1045
1046 /**
1047 * Returns the portion of the text that is selected.
1048 *
1049 * @return the text, null if no selection
1050 */
1051 public String getSelectedText() {
1052 String selText = TextComponent.this.getSelectedText();
1053 // Fix for 4256662
1054 if (selText == null || selText.equals("")) {
1055 return null;
1056 }
1057 return selText;
1058 }
1059
1060 /**
1061 * Returns the String at a given index.
1062 *
1063 * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1064 * or AccessibleText.SENTENCE to retrieve
1065 * @param index an index within the text >= 0
1066 * @return the letter, word, or sentence,
1067 * null for an invalid index or part
1068 */
1069 public String getAtIndex(int part, int index) {
1070 if (index < 0 || index >= TextComponent.this.getText().length()) {
1071 return null;
1072 }
1073 switch (part) {
1074 case AccessibleText.CHARACTER:
1075 return TextComponent.this.getText().substring(index, index+1);
1076 case AccessibleText.WORD: {
1077 String s = TextComponent.this.getText();
1078 BreakIterator words = BreakIterator.getWordInstance();
1079 words.setText(s);
1080 int end = words.following(index);
1081 return s.substring(words.previous(), end);
1082 }
1083 case AccessibleText.SENTENCE: {
1084 String s = TextComponent.this.getText();
1085 BreakIterator sentence = BreakIterator.getSentenceInstance();
1086 sentence.setText(s);
1087 int end = sentence.following(index);
1088 return s.substring(sentence.previous(), end);
1089 }
1090 default:
1091 return null;
1092 }
1093 }
1094
1095 private static final boolean NEXT = true;
1096 private static final boolean PREVIOUS = false;
1097
1098 /**
1099 * Needed to unify forward and backward searching.
1100 * The method assumes that s is the text assigned to words.
1101 */
1102 private int findWordLimit(int index, BreakIterator words, boolean direction,
1103 String s) {
1104 // Fix for 4256660 and 4256661.
1105 // Words iterator is different from character and sentence iterators
1106 // in that end of one word is not necessarily start of another word.
1107 // Please see java.text.BreakIterator JavaDoc. The code below is
1108 // based on nextWordStartAfter example from BreakIterator.java.
1109 int last = (direction == NEXT) ? words.following(index)
1110 : words.preceding(index);
1111 int current = (direction == NEXT) ? words.next()
1112 : words.previous();
1113 while (current != BreakIterator.DONE) {
1114 for (int p = Math.min(last, current); p < Math.max(last, current); p++) {
1115 if (Character.isLetter(s.charAt(p))) {
1116 return last;
1117 }
1118 }
1119 last = current;
1120 current = (direction == NEXT) ? words.next()
1121 : words.previous();
1122 }
1123 return BreakIterator.DONE;
1124 }
1125
1126 /**
1127 * Returns the String after a given index.
1128 *
1129 * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1130 * or AccessibleText.SENTENCE to retrieve
1131 * @param index an index within the text >= 0
1132 * @return the letter, word, or sentence, null for an invalid
1133 * index or part
1134 */
1135 public String getAfterIndex(int part, int index) {
1136 if (index < 0 || index >= TextComponent.this.getText().length()) {
1137 return null;
1138 }
1139 switch (part) {
1140 case AccessibleText.CHARACTER:
1141 if (index+1 >= TextComponent.this.getText().length()) {
1142 return null;
1143 }
1144 return TextComponent.this.getText().substring(index+1, index+2);
1145 case AccessibleText.WORD: {
1146 String s = TextComponent.this.getText();
1147 BreakIterator words = BreakIterator.getWordInstance();
1148 words.setText(s);
1149 int start = findWordLimit(index, words, NEXT, s);
1150 if (start == BreakIterator.DONE || start >= s.length()) {
1151 return null;
1152 }
1153 int end = words.following(start);
1154 if (end == BreakIterator.DONE || end >= s.length()) {
1155 return null;
1156 }
1157 return s.substring(start, end);
1158 }
1159 case AccessibleText.SENTENCE: {
1160 String s = TextComponent.this.getText();
1161 BreakIterator sentence = BreakIterator.getSentenceInstance();
1162 sentence.setText(s);
1163 int start = sentence.following(index);
1164 if (start == BreakIterator.DONE || start >= s.length()) {
1165 return null;
1166 }
1167 int end = sentence.following(start);
1168 if (end == BreakIterator.DONE || end >= s.length()) {
1169 return null;
1170 }
1171 return s.substring(start, end);
1172 }
1173 default:
1174 return null;
1175 }
1176 }
1177
1178
1179 /**
1180 * Returns the String before a given index.
1181 *
1182 * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
1183 * or AccessibleText.SENTENCE to retrieve
1184 * @param index an index within the text >= 0
1185 * @return the letter, word, or sentence, null for an invalid index
1186 * or part
1187 */
1188 public String getBeforeIndex(int part, int index) {
1189 if (index < 0 || index > TextComponent.this.getText().length()-1) {
1190 return null;
1191 }
1192 switch (part) {
1193 case AccessibleText.CHARACTER:
1194 if (index == 0) {
1195 return null;
1196 }
1197 return TextComponent.this.getText().substring(index-1, index);
1198 case AccessibleText.WORD: {
1199 String s = TextComponent.this.getText();
1200 BreakIterator words = BreakIterator.getWordInstance();
1201 words.setText(s);
1202 int end = findWordLimit(index, words, PREVIOUS, s);
1203 if (end == BreakIterator.DONE) {
1204 return null;
1205 }
1206 int start = words.preceding(end);
1207 if (start == BreakIterator.DONE) {
1208 return null;
1209 }
1210 return s.substring(start, end);
1211 }
1212 case AccessibleText.SENTENCE: {
1213 String s = TextComponent.this.getText();
1214 BreakIterator sentence = BreakIterator.getSentenceInstance();
1215 sentence.setText(s);
1216 int end = sentence.following(index);
1217 end = sentence.previous();
1218 int start = sentence.previous();
1219 if (start == BreakIterator.DONE) {
1220 return null;
1221 }
1222 return s.substring(start, end);
1223 }
1224 default:
1225 return null;
1226 }
1227 }
1228 } // end of AccessibleAWTTextComponent
1229
1230 private boolean checkForEnableIM = true;
1231 }