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