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