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:     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