Source for java.awt.TextComponent

   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