GNU Classpath (0.18) | ||
Frames | No Frames |
1: /* TextComponent.java -- Widgets for entering text 2: Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.awt; 40: 41: import java.awt.event.TextEvent; 42: import java.awt.event.TextListener; 43: import java.awt.peer.TextComponentPeer; 44: import java.io.Serializable; 45: import java.text.BreakIterator; 46: import java.util.EventListener; 47: 48: import javax.accessibility.Accessible; 49: import javax.accessibility.AccessibleContext; 50: import javax.accessibility.AccessibleRole; 51: import javax.accessibility.AccessibleState; 52: import javax.accessibility.AccessibleStateSet; 53: import javax.accessibility.AccessibleText; 54: import javax.swing.text.AttributeSet; 55: 56: /** 57: * This class provides common functionality for widgets than 58: * contain text. 59: * 60: * @author Aaron M. Renn (arenn@urbanophile.com) 61: */ 62: public class TextComponent extends Component 63: implements Serializable, Accessible 64: { 65: 66: /* 67: * Static Variables 68: */ 69: 70: // Constant for serialization 71: private static final long serialVersionUID = -2214773872412987419L; 72: 73: /* 74: * Instance Variables 75: */ 76: 77: /** 78: * @serial Indicates whether or not this component is editable. 79: * This is package-private to avoid an accessor method. 80: */ 81: boolean editable; 82: 83: /** 84: * @serial The starting position of the selected text region. 85: * This is package-private to avoid an accessor method. 86: */ 87: int selectionStart; 88: 89: /** 90: * @serial The ending position of the selected text region. 91: * This is package-private to avoid an accessor method. 92: */ 93: int selectionEnd; 94: 95: /** 96: * @serial The text in the component 97: * This is package-private to avoid an accessor method. 98: */ 99: String text; 100: 101: /** 102: * A list of listeners that will receive events from this object. 103: */ 104: protected transient TextListener textListener; 105: 106: protected class AccessibleAWTTextComponent 107: extends AccessibleAWTComponent 108: implements AccessibleText, TextListener 109: { 110: // Constructor 111: // Adds a listener for tracking caret changes 112: public AccessibleAWTTextComponent() 113: { 114: TextComponent.this.addTextListener(this); 115: } 116: 117: public AccessibleRole getAccessibleRole() 118: { 119: return AccessibleRole.TEXT; 120: } 121: 122: public AccessibleStateSet getAccessibleStateSet() 123: { 124: // TODO: Docs say PropertyChangeEvent will fire if this state changes. 125: // That means that the event has to fire when editable changes. 126: AccessibleStateSet ss = super.getAccessibleStateSet(); 127: if (editable) 128: ss.add(AccessibleState.EDITABLE); 129: return ss; 130: } 131: 132: public AccessibleText getAccessibleText() 133: { 134: return this; 135: } 136: 137: /* (non-Javadoc) 138: * @see javax.accessibility.AccessibleText#getIndexAtPoint(java.awt.Point) 139: */ 140: public int getIndexAtPoint(Point point) 141: { 142: return TextComponent.this.getIndexAtPoint(point); 143: } 144: 145: /* (non-Javadoc) 146: * @see javax.accessibility.AccessibleText#getCharacterBounds(int) 147: */ 148: public Rectangle getCharacterBounds(int index) 149: { 150: return TextComponent.this.getCharacterBounds(index); 151: } 152: 153: /* (non-Javadoc) 154: * @see javax.accessibility.AccessibleText#getCharCount() 155: */ 156: public int getCharCount() 157: { 158: return text.length(); 159: } 160: 161: /* (non-Javadoc) 162: * @see javax.accessibility.AccessibleText#getCaretPosition() 163: */ 164: public int getCaretPosition() 165: { 166: return TextComponent.this.getCaretPosition(); 167: } 168: 169: /* (non-Javadoc) 170: * @see javax.accessibility.AccessibleText#getAtIndex(int, int) 171: */ 172: public String getAtIndex(int part, int index) 173: { 174: if (index < 0 || index >= text.length()) 175: return null; 176: BreakIterator it = null; 177: switch (part) 178: { 179: case CHARACTER: 180: return text.substring(index, index + 1); 181: case WORD: 182: it = BreakIterator.getWordInstance(); 183: break; 184: case SENTENCE: 185: it = BreakIterator.getSentenceInstance(); 186: break; 187: default: 188: return null; 189: } 190: it.setText(text); 191: int start = index; 192: if (!it.isBoundary(index)) 193: start = it.preceding(index); 194: int end = it.following(index); 195: if (end == -1) 196: return text.substring(index); 197: else 198: return text.substring(index, end); 199: } 200: 201: /* (non-Javadoc) 202: * @see javax.accessibility.AccessibleText#getAfterIndex(int, int) 203: */ 204: public String getAfterIndex(int part, int index) { 205: if (index < 0 || index >= text.length()) 206: return null; 207: BreakIterator it = null; 208: switch (part) 209: { 210: case CHARACTER: 211: return text.substring(index, index + 1); 212: case WORD: 213: it = BreakIterator.getWordInstance(); 214: break; 215: case SENTENCE: 216: it = BreakIterator.getSentenceInstance(); 217: break; 218: default: 219: return null; 220: } 221: it.setText(text); 222: int start = index; 223: if (!it.isBoundary(index)) 224: start = it.following(index); 225: // Make sure there was a complete unit. I.e. if index is in the middle 226: // of a word, return null if there is no word after the that one. 227: if (start == -1) 228: return null; 229: int end = it.following(start); 230: if (end == -1) 231: return text.substring(index); 232: else 233: return text.substring(index, end); 234: } 235: 236: /* (non-Javadoc) 237: * @see javax.accessibility.AccessibleText#getBeforeIndex(int, int) 238: */ 239: public String getBeforeIndex(int part, int index) 240: { 241: if (index < 1 || index >= text.length()) 242: return null; 243: BreakIterator it = null; 244: switch (part) 245: { 246: case CHARACTER: 247: return text.substring(index - 1, index); 248: case WORD: 249: it = BreakIterator.getWordInstance(); 250: break; 251: case SENTENCE: 252: it = BreakIterator.getSentenceInstance(); 253: break; 254: default: 255: return null; 256: } 257: it.setText(text); 258: int end = index; 259: if (!it.isBoundary(index)) 260: end = it.preceding(index); 261: // Make sure there was a complete unit. I.e. if index is in the middle 262: // of a word, return null if there is no word before that one. 263: if (end == -1) 264: return null; 265: int start = it.preceding(end); 266: if (start == -1) 267: return text.substring(0, end); 268: else 269: return text.substring(start, end); 270: } 271: 272: /* (non-Javadoc) 273: * @see javax.accessibility.AccessibleText#getCharacterAttribute(int) 274: */ 275: public AttributeSet getCharacterAttribute(int index) 276: { 277: // FIXME: I suspect this really gets filled in by subclasses. 278: return null; 279: } 280: 281: /* (non-Javadoc) 282: * @see javax.accessibility.AccessibleText#getSelectionStart() 283: */ 284: public int getSelectionStart() { 285: // TODO Auto-generated method stub 286: return selectionStart; 287: } 288: 289: /* (non-Javadoc) 290: * @see javax.accessibility.AccessibleText#getSelectionEnd() 291: */ 292: public int getSelectionEnd() 293: { 294: return selectionEnd; 295: } 296: 297: /* (non-Javadoc) 298: * @see javax.accessibility.AccessibleText#getSelectedText() 299: */ 300: public String getSelectedText() 301: { 302: if (selectionEnd - selectionStart > 0) 303: return text.substring(selectionStart, selectionEnd); 304: else 305: return null; 306: } 307: 308: /* (non-Javadoc) 309: * @see java.awt.event.TextListener#textValueChanged(java.awt.event.TextEvent) 310: */ 311: public void textValueChanged(TextEvent event) 312: { 313: // TODO Auto-generated method stub 314: 315: } 316: 317: } 318: 319: /*************************************************************************/ 320: 321: /* 322: * Constructors 323: */ 324: 325: TextComponent(String text) 326: { 327: this.text = text; 328: this.editable = true; 329: } 330: 331: /*************************************************************************/ 332: 333: /* 334: * Instance Methods 335: */ 336: 337: /** 338: * Returns the text in this component 339: * 340: * @return The text in this component. 341: */ 342: public synchronized String 343: getText() 344: { 345: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 346: if (tcp != null) 347: text = tcp.getText(); 348: 349: return(text); 350: } 351: 352: /*************************************************************************/ 353: 354: /** 355: * Sets the text in this component to the specified string. 356: * 357: * @param text The new text for this component. 358: */ 359: public synchronized void 360: setText(String text) 361: { 362: if (text == null) 363: text = ""; 364: 365: this.text = text; 366: 367: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 368: if (tcp != null) 369: tcp.setText(text); 370: setCaretPosition(0); 371: } 372: 373: /*************************************************************************/ 374: 375: /** 376: * Returns a string that contains the text that is currently selected. 377: * 378: * @return The currently selected text region. 379: */ 380: public synchronized String 381: getSelectedText() 382: { 383: String alltext = getText(); 384: int start = getSelectionStart(); 385: int end = getSelectionEnd(); 386: 387: return(alltext.substring(start, end)); 388: } 389: 390: /*************************************************************************/ 391: 392: /** 393: * Returns the starting position of the selected text region. 394: * If the text is not selected then caret position is returned. 395: * 396: * @return The starting position of the selected text region. 397: */ 398: public synchronized int 399: getSelectionStart() 400: { 401: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 402: if (tcp != null) 403: selectionStart = tcp.getSelectionStart(); 404: 405: return(selectionStart); 406: } 407: 408: /*************************************************************************/ 409: 410: /** 411: * Sets the starting position of the selected region to the 412: * specified value. If the specified value is out of range, then it 413: * will be silently changed to the nearest legal value. 414: * 415: * @param selectionStart The new start position for selected text. 416: */ 417: public synchronized void 418: setSelectionStart(int selectionStart) 419: { 420: select(selectionStart, getSelectionEnd()); 421: } 422: 423: /*************************************************************************/ 424: 425: /** 426: * Returns the ending position of the selected text region. 427: * If the text is not selected, then caret position is returned 428: * 429: * @return The ending position of the selected text region. 430: */ 431: public synchronized int 432: getSelectionEnd() 433: { 434: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 435: if (tcp != null) 436: selectionEnd = tcp.getSelectionEnd(); 437: 438: return(selectionEnd); 439: } 440: 441: /*************************************************************************/ 442: 443: /** 444: * Sets the ending position of the selected region to the 445: * specified value. If the specified value is out of range, then it 446: * will be silently changed to the nearest legal value. 447: * 448: * @param selectionEnd The new start position for selected text. 449: */ 450: public synchronized void 451: setSelectionEnd(int selectionEnd) 452: { 453: select(getSelectionStart(), selectionEnd); 454: } 455: 456: /*************************************************************************/ 457: 458: /** 459: * This method sets the selected text range to the text between the 460: * specified start and end positions. Illegal values for these 461: * positions are silently fixed. 462: * 463: * @param selectionStart The new start position for the selected text. 464: * @param selectionEnd The new end position for the selected text. 465: */ 466: public synchronized void 467: select(int selectionStart, int selectionEnd) 468: { 469: if (selectionStart < 0) 470: selectionStart = 0; 471: 472: if (selectionStart > getText().length()) 473: selectionStart = text.length(); 474: 475: if (selectionEnd > text.length()) 476: selectionEnd = text.length(); 477: 478: if (selectionStart > selectionEnd) 479: selectionStart = selectionEnd; 480: 481: this.selectionStart = selectionStart; 482: this.selectionEnd = selectionEnd; 483: 484: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 485: if (tcp != null) 486: tcp.select(selectionStart, selectionEnd); 487: } 488: 489: /*************************************************************************/ 490: 491: /** 492: * Selects all of the text in the component. 493: */ 494: public synchronized void 495: selectAll() 496: { 497: select(0, getText().length()); 498: } 499: 500: /*************************************************************************/ 501: 502: /** 503: * Returns the current caret position in the text. 504: * 505: * @return The caret position in the text. 506: */ 507: public synchronized int 508: getCaretPosition() 509: { 510: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 511: if (tcp != null) 512: return(tcp.getCaretPosition()); 513: else 514: return(0); 515: } 516: 517: /*************************************************************************/ 518: 519: /** 520: * Sets the caret position to the specified value. 521: * 522: * @param caretPosition The new caret position. 523: * 524: * @exception IllegalArgumentException If the value supplied for position 525: * is less than zero. 526: * 527: * @since 1.1 528: */ 529: public synchronized void 530: setCaretPosition(int caretPosition) 531: { 532: if (caretPosition < 0) 533: throw new IllegalArgumentException (); 534: 535: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 536: if (tcp != null) 537: tcp.setCaretPosition(caretPosition); 538: } 539: 540: /*************************************************************************/ 541: 542: /** 543: * Tests whether or not this component's text can be edited. 544: * 545: * @return <code>true</code> if the text can be edited, <code>false</code> 546: * otherwise. 547: */ 548: public boolean 549: isEditable() 550: { 551: return(editable); 552: } 553: 554: /*************************************************************************/ 555: 556: /** 557: * Sets whether or not this component's text can be edited. 558: * 559: * @param editable <code>true</code> to enable editing of the text, 560: * <code>false</code> to disable it. 561: */ 562: public synchronized void 563: setEditable(boolean editable) 564: { 565: this.editable = editable; 566: 567: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 568: if (tcp != null) 569: tcp.setEditable(editable); 570: } 571: 572: /*************************************************************************/ 573: 574: /** 575: * Notifies the component that it should destroy its native peer. 576: */ 577: public void 578: removeNotify() 579: { 580: super.removeNotify(); 581: } 582: 583: /*************************************************************************/ 584: 585: /** 586: * Adds a new listener to the list of text listeners for this 587: * component. 588: * 589: * @param listener The listener to be added. 590: */ 591: public synchronized void 592: addTextListener(TextListener listener) 593: { 594: textListener = AWTEventMulticaster.add(textListener, listener); 595: 596: enableEvents(AWTEvent.TEXT_EVENT_MASK); 597: } 598: 599: /*************************************************************************/ 600: 601: /** 602: * Removes the specified listener from the list of listeners 603: * for this component. 604: * 605: * @param listener The listener to remove. 606: */ 607: public synchronized void 608: removeTextListener(TextListener listener) 609: { 610: textListener = AWTEventMulticaster.remove(textListener, listener); 611: } 612: 613: /*************************************************************************/ 614: 615: /** 616: * Processes the specified event for this component. Text events are 617: * processed by calling the <code>processTextEvent()</code> method. 618: * All other events are passed to the superclass method. 619: * 620: * @param event The event to process. 621: */ 622: protected void 623: processEvent(AWTEvent event) 624: { 625: if (event instanceof TextEvent) 626: processTextEvent((TextEvent)event); 627: else 628: super.processEvent(event); 629: } 630: 631: /*************************************************************************/ 632: 633: /** 634: * Processes the specified text event by dispatching it to any listeners 635: * that are registered. Note that this method will only be called 636: * if text event's are enabled. This will be true if there are any 637: * registered listeners, or if the event has been specifically 638: * enabled using <code>enableEvents()</code>. 639: * 640: * @param event The text event to process. 641: */ 642: protected void 643: processTextEvent(TextEvent event) 644: { 645: if (textListener != null) 646: textListener.textValueChanged(event); 647: } 648: 649: void 650: dispatchEventImpl(AWTEvent e) 651: { 652: if (e.id <= TextEvent.TEXT_LAST 653: && e.id >= TextEvent.TEXT_FIRST 654: && (textListener != null 655: || (eventMask & AWTEvent.TEXT_EVENT_MASK) != 0)) 656: processEvent(e); 657: else 658: super.dispatchEventImpl(e); 659: } 660: 661: /*************************************************************************/ 662: 663: /** 664: * Returns a debugging string. 665: * 666: * @return A debugging string. 667: */ 668: protected String 669: paramString() 670: { 671: return(getClass().getName() + "(text=" + getText() + ")"); 672: } 673: 674: /** 675: * Returns an array of all the objects currently registered as FooListeners 676: * upon this <code>TextComponent</code>. FooListeners are registered using 677: * the addFooListener method. 678: * 679: * @exception ClassCastException If listenerType doesn't specify a class or 680: * interface that implements java.util.EventListener. 681: */ 682: public EventListener[] getListeners (Class listenerType) 683: { 684: if (listenerType == TextListener.class) 685: return AWTEventMulticaster.getListeners (textListener, listenerType); 686: 687: return super.getListeners (listenerType); 688: } 689: 690: /** 691: * Returns all text listeners registered to this object. 692: */ 693: public TextListener[] getTextListeners () 694: { 695: return (TextListener[]) getListeners (TextListener.class); 696: } 697: 698: /** 699: * Gets the AccessibleContext associated with this <code>TextComponent</code>. 700: * The context is created, if necessary. 701: * 702: * @return the associated context 703: */ 704: public AccessibleContext getAccessibleContext() 705: { 706: /* Create the context if this is the first request */ 707: if (accessibleContext == null) 708: accessibleContext = new AccessibleAWTTextComponent(); 709: return accessibleContext; 710: } 711: 712: 713: /*******************************/ 714: // Provide AccessibleAWTTextComponent access to several peer functions that 715: // aren't publicly exposed. This is package-private to avoid an accessor 716: // method. 717: synchronized int 718: getIndexAtPoint(Point p) 719: { 720: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 721: if (tcp != null) 722: return tcp.getIndexAtPoint(p.x, p.y); 723: return -1; 724: } 725: 726: synchronized Rectangle 727: getCharacterBounds(int i) 728: { 729: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 730: if (tcp != null) 731: return tcp.getCharacterBounds(i); 732: return null; 733: } 734: 735: 736: 737: 738: } // class TextComponent
GNU Classpath (0.18) |