GNU Classpath (0.20) | |
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: private static final long serialVersionUID = 3631432373506317811L; 111: 112: // Constructor 113: // Adds a listener for tracking caret changes 114: public AccessibleAWTTextComponent() 115: { 116: TextComponent.this.addTextListener(this); 117: } 118: 119: public AccessibleRole getAccessibleRole() 120: { 121: return AccessibleRole.TEXT; 122: } 123: 124: public AccessibleStateSet getAccessibleStateSet() 125: { 126: // TODO: Docs say PropertyChangeEvent will fire if this state changes. 127: // That means that the event has to fire when editable changes. 128: AccessibleStateSet ss = super.getAccessibleStateSet(); 129: if (editable) 130: ss.add(AccessibleState.EDITABLE); 131: return ss; 132: } 133: 134: public AccessibleText getAccessibleText() 135: { 136: return this; 137: } 138: 139: /* (non-Javadoc) 140: * @see javax.accessibility.AccessibleText#getIndexAtPoint(java.awt.Point) 141: */ 142: public int getIndexAtPoint(Point point) 143: { 144: return TextComponent.this.getIndexAtPoint(point); 145: } 146: 147: /* (non-Javadoc) 148: * @see javax.accessibility.AccessibleText#getCharacterBounds(int) 149: */ 150: public Rectangle getCharacterBounds(int index) 151: { 152: return TextComponent.this.getCharacterBounds(index); 153: } 154: 155: /* (non-Javadoc) 156: * @see javax.accessibility.AccessibleText#getCharCount() 157: */ 158: public int getCharCount() 159: { 160: return text.length(); 161: } 162: 163: /* (non-Javadoc) 164: * @see javax.accessibility.AccessibleText#getCaretPosition() 165: */ 166: public int getCaretPosition() 167: { 168: return TextComponent.this.getCaretPosition(); 169: } 170: 171: /* (non-Javadoc) 172: * @see javax.accessibility.AccessibleText#getAtIndex(int, int) 173: */ 174: public String getAtIndex(int part, int index) 175: { 176: if (index < 0 || index >= text.length()) 177: return null; 178: BreakIterator it = null; 179: switch (part) 180: { 181: case CHARACTER: 182: return text.substring(index, index + 1); 183: case WORD: 184: it = BreakIterator.getWordInstance(); 185: break; 186: case SENTENCE: 187: it = BreakIterator.getSentenceInstance(); 188: break; 189: default: 190: return null; 191: } 192: it.setText(text); 193: int start = index; 194: if (!it.isBoundary(index)) 195: start = it.preceding(index); 196: int end = it.following(index); 197: if (end == -1) 198: return text.substring(index); 199: else 200: return text.substring(index, end); 201: } 202: 203: /* (non-Javadoc) 204: * @see javax.accessibility.AccessibleText#getAfterIndex(int, int) 205: */ 206: public String getAfterIndex(int part, int index) { 207: if (index < 0 || index >= text.length()) 208: return null; 209: BreakIterator it = null; 210: switch (part) 211: { 212: case CHARACTER: 213: return text.substring(index, index + 1); 214: case WORD: 215: it = BreakIterator.getWordInstance(); 216: break; 217: case SENTENCE: 218: it = BreakIterator.getSentenceInstance(); 219: break; 220: default: 221: return null; 222: } 223: it.setText(text); 224: int start = index; 225: if (!it.isBoundary(index)) 226: start = it.following(index); 227: // Make sure there was a complete unit. I.e. if index is in the middle 228: // of a word, return null if there is no word after the that one. 229: if (start == -1) 230: return null; 231: int end = it.following(start); 232: if (end == -1) 233: return text.substring(index); 234: else 235: return text.substring(index, end); 236: } 237: 238: /* (non-Javadoc) 239: * @see javax.accessibility.AccessibleText#getBeforeIndex(int, int) 240: */ 241: public String getBeforeIndex(int part, int index) 242: { 243: if (index < 1 || index >= text.length()) 244: return null; 245: BreakIterator it = null; 246: switch (part) 247: { 248: case CHARACTER: 249: return text.substring(index - 1, index); 250: case WORD: 251: it = BreakIterator.getWordInstance(); 252: break; 253: case SENTENCE: 254: it = BreakIterator.getSentenceInstance(); 255: break; 256: default: 257: return null; 258: } 259: it.setText(text); 260: int end = index; 261: if (!it.isBoundary(index)) 262: end = it.preceding(index); 263: // Make sure there was a complete unit. I.e. if index is in the middle 264: // of a word, return null if there is no word before that one. 265: if (end == -1) 266: return null; 267: int start = it.preceding(end); 268: if (start == -1) 269: return text.substring(0, end); 270: else 271: return text.substring(start, end); 272: } 273: 274: /* (non-Javadoc) 275: * @see javax.accessibility.AccessibleText#getCharacterAttribute(int) 276: */ 277: public AttributeSet getCharacterAttribute(int index) 278: { 279: // FIXME: I suspect this really gets filled in by subclasses. 280: return null; 281: } 282: 283: /* (non-Javadoc) 284: * @see javax.accessibility.AccessibleText#getSelectionStart() 285: */ 286: public int getSelectionStart() { 287: // TODO Auto-generated method stub 288: return selectionStart; 289: } 290: 291: /* (non-Javadoc) 292: * @see javax.accessibility.AccessibleText#getSelectionEnd() 293: */ 294: public int getSelectionEnd() 295: { 296: return selectionEnd; 297: } 298: 299: /* (non-Javadoc) 300: * @see javax.accessibility.AccessibleText#getSelectedText() 301: */ 302: public String getSelectedText() 303: { 304: if (selectionEnd - selectionStart > 0) 305: return text.substring(selectionStart, selectionEnd); 306: else 307: return null; 308: } 309: 310: /* (non-Javadoc) 311: * @see java.awt.event.TextListener#textValueChanged(java.awt.event.TextEvent) 312: */ 313: public void textValueChanged(TextEvent event) 314: { 315: // TODO Auto-generated method stub 316: 317: } 318: 319: } 320: 321: /*************************************************************************/ 322: 323: /* 324: * Constructors 325: */ 326: 327: TextComponent(String text) 328: { 329: this.text = text; 330: this.editable = true; 331: } 332: 333: /*************************************************************************/ 334: 335: /* 336: * Instance Methods 337: */ 338: 339: /** 340: * Returns the text in this component 341: * 342: * @return The text in this component. 343: */ 344: public synchronized String 345: getText() 346: { 347: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 348: if (tcp != null) 349: text = tcp.getText(); 350: 351: return(text); 352: } 353: 354: /*************************************************************************/ 355: 356: /** 357: * Sets the text in this component to the specified string. 358: * 359: * @param text The new text for this component. 360: */ 361: public synchronized void 362: setText(String text) 363: { 364: if (text == null) 365: text = ""; 366: 367: this.text = text; 368: 369: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 370: if (tcp != null) 371: tcp.setText(text); 372: setCaretPosition(0); 373: } 374: 375: /*************************************************************************/ 376: 377: /** 378: * Returns a string that contains the text that is currently selected. 379: * 380: * @return The currently selected text region. 381: */ 382: public synchronized String 383: getSelectedText() 384: { 385: String alltext = getText(); 386: int start = getSelectionStart(); 387: int end = getSelectionEnd(); 388: 389: return(alltext.substring(start, end)); 390: } 391: 392: /*************************************************************************/ 393: 394: /** 395: * Returns the starting position of the selected text region. 396: * If the text is not selected then caret position is returned. 397: * 398: * @return The starting position of the selected text region. 399: */ 400: public synchronized int 401: getSelectionStart() 402: { 403: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 404: if (tcp != null) 405: selectionStart = tcp.getSelectionStart(); 406: 407: return(selectionStart); 408: } 409: 410: /*************************************************************************/ 411: 412: /** 413: * Sets the starting position of the selected region to the 414: * specified value. If the specified value is out of range, then it 415: * will be silently changed to the nearest legal value. 416: * 417: * @param selectionStart The new start position for selected text. 418: */ 419: public synchronized void 420: setSelectionStart(int selectionStart) 421: { 422: select(selectionStart, getSelectionEnd()); 423: } 424: 425: /*************************************************************************/ 426: 427: /** 428: * Returns the ending position of the selected text region. 429: * If the text is not selected, then caret position is returned 430: * 431: * @return The ending position of the selected text region. 432: */ 433: public synchronized int 434: getSelectionEnd() 435: { 436: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 437: if (tcp != null) 438: selectionEnd = tcp.getSelectionEnd(); 439: 440: return(selectionEnd); 441: } 442: 443: /*************************************************************************/ 444: 445: /** 446: * Sets the ending position of the selected region to the 447: * specified value. If the specified value is out of range, then it 448: * will be silently changed to the nearest legal value. 449: * 450: * @param selectionEnd The new start position for selected text. 451: */ 452: public synchronized void 453: setSelectionEnd(int selectionEnd) 454: { 455: select(getSelectionStart(), selectionEnd); 456: } 457: 458: /*************************************************************************/ 459: 460: /** 461: * This method sets the selected text range to the text between the 462: * specified start and end positions. Illegal values for these 463: * positions are silently fixed. 464: * 465: * @param selectionStart The new start position for the selected text. 466: * @param selectionEnd The new end position for the selected text. 467: */ 468: public synchronized void 469: select(int selectionStart, int selectionEnd) 470: { 471: if (selectionStart < 0) 472: selectionStart = 0; 473: 474: if (selectionStart > getText().length()) 475: selectionStart = text.length(); 476: 477: if (selectionEnd > text.length()) 478: selectionEnd = text.length(); 479: 480: if (selectionStart > selectionEnd) 481: selectionStart = selectionEnd; 482: 483: this.selectionStart = selectionStart; 484: this.selectionEnd = selectionEnd; 485: 486: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 487: if (tcp != null) 488: tcp.select(selectionStart, selectionEnd); 489: } 490: 491: /*************************************************************************/ 492: 493: /** 494: * Selects all of the text in the component. 495: */ 496: public synchronized void 497: selectAll() 498: { 499: select(0, getText().length()); 500: } 501: 502: /*************************************************************************/ 503: 504: /** 505: * Returns the current caret position in the text. 506: * 507: * @return The caret position in the text. 508: */ 509: public synchronized int 510: getCaretPosition() 511: { 512: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 513: if (tcp != null) 514: return(tcp.getCaretPosition()); 515: else 516: return(0); 517: } 518: 519: /*************************************************************************/ 520: 521: /** 522: * Sets the caret position to the specified value. 523: * 524: * @param caretPosition The new caret position. 525: * 526: * @exception IllegalArgumentException If the value supplied for position 527: * is less than zero. 528: * 529: * @since 1.1 530: */ 531: public synchronized void 532: setCaretPosition(int caretPosition) 533: { 534: if (caretPosition < 0) 535: throw new IllegalArgumentException (); 536: 537: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 538: if (tcp != null) 539: tcp.setCaretPosition(caretPosition); 540: } 541: 542: /*************************************************************************/ 543: 544: /** 545: * Tests whether or not this component's text can be edited. 546: * 547: * @return <code>true</code> if the text can be edited, <code>false</code> 548: * otherwise. 549: */ 550: public boolean 551: isEditable() 552: { 553: return(editable); 554: } 555: 556: /*************************************************************************/ 557: 558: /** 559: * Sets whether or not this component's text can be edited. 560: * 561: * @param editable <code>true</code> to enable editing of the text, 562: * <code>false</code> to disable it. 563: */ 564: public synchronized void 565: setEditable(boolean editable) 566: { 567: this.editable = editable; 568: 569: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 570: if (tcp != null) 571: tcp.setEditable(editable); 572: } 573: 574: /*************************************************************************/ 575: 576: /** 577: * Notifies the component that it should destroy its native peer. 578: */ 579: public void 580: removeNotify() 581: { 582: super.removeNotify(); 583: } 584: 585: /*************************************************************************/ 586: 587: /** 588: * Adds a new listener to the list of text listeners for this 589: * component. 590: * 591: * @param listener The listener to be added. 592: */ 593: public synchronized void 594: addTextListener(TextListener listener) 595: { 596: textListener = AWTEventMulticaster.add(textListener, listener); 597: 598: enableEvents(AWTEvent.TEXT_EVENT_MASK); 599: } 600: 601: /*************************************************************************/ 602: 603: /** 604: * Removes the specified listener from the list of listeners 605: * for this component. 606: * 607: * @param listener The listener to remove. 608: */ 609: public synchronized void 610: removeTextListener(TextListener listener) 611: { 612: textListener = AWTEventMulticaster.remove(textListener, listener); 613: } 614: 615: /*************************************************************************/ 616: 617: /** 618: * Processes the specified event for this component. Text events are 619: * processed by calling the <code>processTextEvent()</code> method. 620: * All other events are passed to the superclass method. 621: * 622: * @param event The event to process. 623: */ 624: protected void 625: processEvent(AWTEvent event) 626: { 627: if (event instanceof TextEvent) 628: processTextEvent((TextEvent)event); 629: else 630: super.processEvent(event); 631: } 632: 633: /*************************************************************************/ 634: 635: /** 636: * Processes the specified text event by dispatching it to any listeners 637: * that are registered. Note that this method will only be called 638: * if text event's are enabled. This will be true if there are any 639: * registered listeners, or if the event has been specifically 640: * enabled using <code>enableEvents()</code>. 641: * 642: * @param event The text event to process. 643: */ 644: protected void 645: processTextEvent(TextEvent event) 646: { 647: if (textListener != null) 648: textListener.textValueChanged(event); 649: } 650: 651: void 652: dispatchEventImpl(AWTEvent e) 653: { 654: if (e.id <= TextEvent.TEXT_LAST 655: && e.id >= TextEvent.TEXT_FIRST 656: && (textListener != null 657: || (eventMask & AWTEvent.TEXT_EVENT_MASK) != 0)) 658: processEvent(e); 659: else 660: super.dispatchEventImpl(e); 661: } 662: 663: /*************************************************************************/ 664: 665: /** 666: * Returns a debugging string. 667: * 668: * @return A debugging string. 669: */ 670: protected String 671: paramString() 672: { 673: return(getClass().getName() + "(text=" + getText() + ")"); 674: } 675: 676: /** 677: * Returns an array of all the objects currently registered as FooListeners 678: * upon this <code>TextComponent</code>. FooListeners are registered using 679: * the addFooListener method. 680: * 681: * @exception ClassCastException If listenerType doesn't specify a class or 682: * interface that implements java.util.EventListener. 683: */ 684: public EventListener[] getListeners (Class listenerType) 685: { 686: if (listenerType == TextListener.class) 687: return AWTEventMulticaster.getListeners (textListener, listenerType); 688: 689: return super.getListeners (listenerType); 690: } 691: 692: /** 693: * Returns all text listeners registered to this object. 694: */ 695: public TextListener[] getTextListeners () 696: { 697: return (TextListener[]) getListeners (TextListener.class); 698: } 699: 700: /** 701: * Gets the AccessibleContext associated with this <code>TextComponent</code>. 702: * The context is created, if necessary. 703: * 704: * @return the associated context 705: */ 706: public AccessibleContext getAccessibleContext() 707: { 708: /* Create the context if this is the first request */ 709: if (accessibleContext == null) 710: accessibleContext = new AccessibleAWTTextComponent(); 711: return accessibleContext; 712: } 713: 714: 715: /*******************************/ 716: // Provide AccessibleAWTTextComponent access to several peer functions that 717: // aren't publicly exposed. This is package-private to avoid an accessor 718: // method. 719: synchronized int 720: getIndexAtPoint(Point p) 721: { 722: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 723: if (tcp != null) 724: return tcp.getIndexAtPoint(p.x, p.y); 725: return -1; 726: } 727: 728: synchronized Rectangle 729: getCharacterBounds(int i) 730: { 731: TextComponentPeer tcp = (TextComponentPeer)getPeer(); 732: if (tcp != null) 733: return tcp.getCharacterBounds(i); 734: return null; 735: } 736: 737: 738: 739: 740: } // class TextComponent
GNU Classpath (0.20) |