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