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