Source for java.awt.TextComponent

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