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