Source for javax.swing.text.JTextComponent

   1: /* JTextComponent.java --
   2:    Copyright (C) 2002, 2004, 2005  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 javax.swing.text;
  40: 
  41: import java.awt.AWTEvent;
  42: import java.awt.Color;
  43: import java.awt.Dimension;
  44: import java.awt.Insets;
  45: import java.awt.Point;
  46: import java.awt.Rectangle;
  47: import java.awt.datatransfer.Clipboard;
  48: import java.awt.datatransfer.DataFlavor;
  49: import java.awt.datatransfer.StringSelection;
  50: import java.awt.datatransfer.Transferable;
  51: import java.awt.datatransfer.UnsupportedFlavorException;
  52: import java.awt.event.ActionEvent;
  53: import java.awt.event.InputMethodListener;
  54: import java.awt.event.KeyEvent;
  55: import java.awt.event.MouseEvent;
  56: import java.io.IOException;
  57: import java.io.Reader;
  58: import java.io.Writer;
  59: import java.util.Enumeration;
  60: import java.util.Hashtable;
  61: 
  62: import javax.accessibility.Accessible;
  63: import javax.accessibility.AccessibleContext;
  64: import javax.accessibility.AccessibleRole;
  65: import javax.accessibility.AccessibleStateSet;
  66: import javax.accessibility.AccessibleText;
  67: import javax.swing.Action;
  68: import javax.swing.ActionMap;
  69: import javax.swing.InputMap;
  70: import javax.swing.JComponent;
  71: import javax.swing.JViewport;
  72: import javax.swing.KeyStroke;
  73: import javax.swing.Scrollable;
  74: import javax.swing.SwingConstants;
  75: import javax.swing.TransferHandler;
  76: import javax.swing.UIManager;
  77: import javax.swing.event.CaretEvent;
  78: import javax.swing.event.CaretListener;
  79: import javax.swing.event.DocumentEvent;
  80: import javax.swing.event.DocumentListener;
  81: import javax.swing.plaf.ActionMapUIResource;
  82: import javax.swing.plaf.InputMapUIResource;
  83: import javax.swing.plaf.TextUI;
  84: 
  85: public abstract class JTextComponent extends JComponent
  86:   implements Scrollable, Accessible
  87: {
  88:   /**
  89:    * AccessibleJTextComponent
  90:    */
  91:   // FIXME: This inner class is a complete stub and needs to be implemented.
  92:   public class AccessibleJTextComponent extends AccessibleJComponent
  93:     implements AccessibleText, CaretListener, DocumentListener
  94:   {
  95:     private static final long serialVersionUID = 7664188944091413696L;
  96: 
  97:     /**
  98:      * Constructor AccessibleJTextComponent
  99:      */
 100:     public AccessibleJTextComponent()
 101:     {
 102:       // Nothing to do here.
 103:     }
 104: 
 105:     /**
 106:      * getCaretPosition
 107:      * @return int
 108:      */
 109:     public int getCaretPosition()
 110:     {
 111:       return 0; // TODO
 112:     }
 113: 
 114:     /**
 115:      * getSelectedText
 116:      * @return String
 117:      */
 118:     public String getSelectedText()
 119:     {
 120:       return null; // TODO
 121:     }
 122: 
 123:     /**
 124:      * getSelectionStart
 125:      * @return int
 126:      */
 127:     public int getSelectionStart()
 128:     {
 129:       return 0; // TODO
 130:     }
 131: 
 132:     /**
 133:      * getSelectionEnd
 134:      * @return int
 135:      */
 136:     public int getSelectionEnd()
 137:     {
 138:       return 0; // TODO
 139:     }
 140: 
 141:     /**
 142:      * caretUpdate
 143:      * @param value0 TODO
 144:      */
 145:     public void caretUpdate(CaretEvent value0)
 146:     {
 147:       // TODO
 148:     }
 149: 
 150:     /**
 151:      * getAccessibleStateSet
 152:      * @return AccessibleStateSet
 153:      */
 154:     public AccessibleStateSet getAccessibleStateSet()
 155:     {
 156:       return null; // TODO
 157:     }
 158: 
 159:     /**
 160:      * getAccessibleRole
 161:      * @return AccessibleRole
 162:      */
 163:     public AccessibleRole getAccessibleRole()
 164:     {
 165:       return null; // TODO
 166:     }
 167: 
 168:     /**
 169:      * getAccessibleText
 170:      * @return AccessibleText
 171:      */
 172:     public AccessibleText getAccessibleText()
 173:     {
 174:       return null; // TODO
 175:     }
 176: 
 177:     /**
 178:      * insertUpdate
 179:      * @param value0 TODO
 180:      */
 181:     public void insertUpdate(DocumentEvent value0)
 182:     {
 183:       // TODO
 184:     }
 185: 
 186:     /**
 187:      * removeUpdate
 188:      * @param value0 TODO
 189:      */
 190:     public void removeUpdate(DocumentEvent value0)
 191:     {
 192:       // TODO
 193:     }
 194: 
 195:     /**
 196:      * changedUpdate
 197:      * @param value0 TODO
 198:      */
 199:     public void changedUpdate(DocumentEvent value0)
 200:     {
 201:       // TODO
 202:     }
 203: 
 204:     /**
 205:      * getIndexAtPoint
 206:      * @param value0 TODO
 207:      * @return int
 208:      */
 209:     public int getIndexAtPoint(Point value0)
 210:     {
 211:       return 0; // TODO
 212:     }
 213: 
 214:     /**
 215:      * getRootEditorRect
 216:      * @return Rectangle
 217:      */
 218:     Rectangle getRootEditorRect()
 219:     {
 220:       return null;
 221:     }
 222: 
 223:     /**
 224:      * getCharacterBounds
 225:      * @param value0 TODO
 226:      * @return Rectangle
 227:      */
 228:     public Rectangle getCharacterBounds(int value0)
 229:     {
 230:       return null; // TODO
 231:     }
 232: 
 233:     /**
 234:      * getCharCount
 235:      * @return int
 236:      */
 237:     public int getCharCount()
 238:     {
 239:       return 0; // TODO
 240:     }
 241: 
 242:     /**
 243:      * getCharacterAttribute
 244:      * @param value0 TODO
 245:      * @return AttributeSet
 246:      */
 247:     public AttributeSet getCharacterAttribute(int value0)
 248:     {
 249:       return null; // TODO
 250:     }
 251: 
 252:     /**
 253:      * getAtIndex
 254:      * @param value0 TODO
 255:      * @param value1 TODO
 256:      * @return String
 257:      */
 258:     public String getAtIndex(int value0, int value1)
 259:     {
 260:       return null; // TODO
 261:     }
 262: 
 263:     /**
 264:      * getAfterIndex
 265:      * @param value0 TODO
 266:      * @param value1 TODO
 267:      * @return String
 268:      */
 269:     public String getAfterIndex(int value0, int value1)
 270:     {
 271:       return null; // TODO
 272:     }
 273: 
 274:     /**
 275:      * getBeforeIndex
 276:      * @param value0 TODO
 277:      * @param value1 TODO
 278:      * @return String
 279:      */
 280:     public String getBeforeIndex(int value0, int value1)
 281:     {
 282:       return null; // TODO
 283:     }
 284:   }
 285: 
 286:   public static class KeyBinding
 287:   {
 288:     public KeyStroke key;
 289:     public String actionName;
 290: 
 291:     /**
 292:      * Creates a new <code>KeyBinding</code> instance.
 293:      *
 294:      * @param key a <code>KeyStroke</code> value
 295:      * @param actionName a <code>String</code> value
 296:      */
 297:     public KeyBinding(KeyStroke key, String actionName)
 298:     {
 299:       this.key = key;
 300:       this.actionName = actionName;
 301:     }
 302:   }
 303: 
 304:   /**
 305:    * According to <a
 306:    * href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">this
 307:    * report</a>, a pair of private classes wraps a {@link
 308:    * javax.swing.text.Keymap} in the new {@link InputMap} / {@link
 309:    * ActionMap} interfaces, such that old Keymap-using code can make use of
 310:    * the new framework.
 311:    *
 312:    * <p>A little bit of experimentation with these classes reveals the following
 313:    * structure:
 314:    *
 315:    * <ul>
 316:    *
 317:    * <li>KeymapWrapper extends {@link InputMap} and holds a reference to
 318:    * the underlying {@link Keymap}.</li>
 319:    *
 320:    * <li>KeymapWrapper maps {@link KeyStroke} objects to {@link Action}
 321:    * objects, by delegation to the underlying {@link Keymap}.</li>
 322:    *
 323:    * <li>KeymapActionMap extends {@link ActionMap} also holds a reference to
 324:    * the underlying {@link Keymap} but only appears to use it for listing 
 325:    * its keys. </li>
 326:    *
 327:    * <li>KeymapActionMap maps all {@link Action} objects to
 328:    * <em>themselves</em>, whether they exist in the underlying {@link
 329:    * Keymap} or not, and passes other objects to the parent {@link
 330:    * ActionMap} for resolving.
 331:    *
 332:    * </ul>
 333:    */
 334: 
 335:   private class KeymapWrapper extends InputMap
 336:   {
 337:     Keymap map;
 338: 
 339:     public KeymapWrapper(Keymap k)
 340:     {
 341:       map = k;
 342:     }
 343: 
 344:     public int size()
 345:     {
 346:       return map.getBoundKeyStrokes().length + super.size();
 347:     }
 348: 
 349:     public Object get(KeyStroke ks)
 350:     {
 351:       Action mapped = null;
 352:       Keymap m = map;
 353:       while(mapped == null && m != null)
 354:         {
 355:           mapped = m.getAction(ks);
 356:           if (mapped == null && ks.getKeyEventType() == KeyEvent.KEY_TYPED)
 357:             mapped = m.getDefaultAction();
 358:           if (mapped == null)
 359:             m = m.getResolveParent();
 360:         }
 361: 
 362:       if (mapped == null)
 363:         return super.get(ks);
 364:       else
 365:         return mapped;
 366:     }
 367: 
 368:     public KeyStroke[] keys()
 369:     {
 370:       KeyStroke[] superKeys = super.keys();
 371:       KeyStroke[] mapKeys = map.getBoundKeyStrokes(); 
 372:       KeyStroke[] bothKeys = new KeyStroke[superKeys.length + mapKeys.length];
 373:       for (int i = 0; i < superKeys.length; ++i)
 374:         bothKeys[i] = superKeys[i];
 375:       for (int i = 0; i < mapKeys.length; ++i)
 376:         bothKeys[i + superKeys.length] = mapKeys[i];
 377:       return bothKeys;
 378:     }
 379: 
 380:     public KeyStroke[] allKeys()
 381:     {
 382:       KeyStroke[] superKeys = super.allKeys();
 383:       KeyStroke[] mapKeys = map.getBoundKeyStrokes();
 384:       int skl = 0;
 385:       int mkl = 0;
 386:       if (superKeys != null)
 387:         skl = superKeys.length;
 388:       if (mapKeys != null)
 389:         mkl = mapKeys.length;
 390:       KeyStroke[] bothKeys = new KeyStroke[skl + mkl];
 391:       for (int i = 0; i < skl; ++i)
 392:         bothKeys[i] = superKeys[i];
 393:       for (int i = 0; i < mkl; ++i)
 394:         bothKeys[i + skl] = mapKeys[i];
 395:       return bothKeys;
 396:     }
 397:   }
 398: 
 399:   private class KeymapActionMap extends ActionMap
 400:   {
 401:     Keymap map;
 402: 
 403:     public KeymapActionMap(Keymap k)
 404:     {
 405:       map = k;
 406:     }
 407: 
 408:     public Action get(Object cmd)
 409:     {
 410:       if (cmd instanceof Action)
 411:         return (Action) cmd;
 412:       else
 413:         return super.get(cmd);
 414:     }
 415: 
 416:     public int size()
 417:     {
 418:       return map.getBoundKeyStrokes().length + super.size();
 419:     }
 420: 
 421:     public Object[] keys() 
 422:     {
 423:       Object[] superKeys = super.keys();
 424:       Object[] mapKeys = map.getBoundKeyStrokes(); 
 425:       Object[] bothKeys = new Object[superKeys.length + mapKeys.length];
 426:       for (int i = 0; i < superKeys.length; ++i)
 427:         bothKeys[i] = superKeys[i];
 428:       for (int i = 0; i < mapKeys.length; ++i)
 429:         bothKeys[i + superKeys.length] = mapKeys[i];
 430:       return bothKeys;      
 431:     }
 432: 
 433:     public Object[] allKeys()
 434:     {
 435:       Object[] superKeys = super.allKeys();
 436:       Object[] mapKeys = map.getBoundKeyStrokes(); 
 437:       Object[] bothKeys = new Object[superKeys.length + mapKeys.length];
 438:       for (int i = 0; i < superKeys.length; ++i)
 439:         bothKeys[i] = superKeys[i];
 440:       for (int i = 0; i < mapKeys.length; ++i)
 441:         bothKeys[i + superKeys.length] = mapKeys[i];
 442:       return bothKeys;
 443:     }
 444: 
 445:   }
 446: 
 447:   static class DefaultKeymap implements Keymap
 448:   {
 449:     String name;
 450:     Keymap parent;
 451:     Hashtable map;
 452:     Action defaultAction;
 453: 
 454:     public DefaultKeymap(String name)
 455:     {
 456:       this.name = name;
 457:       this.map = new Hashtable();
 458:     }
 459: 
 460:     public void addActionForKeyStroke(KeyStroke key, Action a)
 461:     {
 462:       map.put(key, a);
 463:     }
 464: 
 465:     /**
 466:      * Looks up a KeyStroke either in the current map or the parent Keymap;
 467:      * does <em>not</em> return the default action if lookup fails.
 468:      *
 469:      * @param key The KeyStroke to look up an Action for.
 470:      *
 471:      * @return The mapping for <code>key</code>, or <code>null</code>
 472:      * if no mapping exists in this Keymap or any of its parents.
 473:      */
 474:     public Action getAction(KeyStroke key)
 475:     {
 476:       if (map.containsKey(key))
 477:         return (Action) map.get(key);
 478:       else if (parent != null)
 479:         return parent.getAction(key);
 480:       else
 481:         return null;
 482:     }
 483: 
 484:     public Action[] getBoundActions()
 485:     {
 486:       Action [] ret = new Action[map.size()];
 487:       Enumeration e = map.elements();
 488:       int i = 0;
 489:       while (e.hasMoreElements())
 490:         {
 491:           ret[i++] = (Action) e.nextElement();
 492:         }
 493:       return ret;
 494:     }
 495: 
 496:     public KeyStroke[] getBoundKeyStrokes()
 497:     {
 498:       KeyStroke [] ret = new KeyStroke[map.size()];
 499:       Enumeration e = map.keys();
 500:       int i = 0;
 501:       while (e.hasMoreElements())
 502:         {
 503:           ret[i++] = (KeyStroke) e.nextElement();
 504:         }
 505:       return ret;
 506:     }
 507: 
 508:     public Action getDefaultAction()
 509:     {
 510:       return defaultAction;
 511:     }
 512: 
 513:     public KeyStroke[] getKeyStrokesForAction(Action a)
 514:     {
 515:       int i = 0;
 516:       Enumeration e = map.keys();
 517:       while (e.hasMoreElements())
 518:         {
 519:           if (map.get(e.nextElement()).equals(a))
 520:             ++i;
 521:         }
 522:       KeyStroke [] ret = new KeyStroke[i];
 523:       i = 0;
 524:       e = map.keys();
 525:       while (e.hasMoreElements())
 526:         {          
 527:           KeyStroke k = (KeyStroke) e.nextElement();
 528:           if (map.get(k).equals(a))
 529:             ret[i++] = k;            
 530:         }
 531:       return ret;
 532:     }
 533: 
 534:     public String getName()
 535:     {
 536:       return name;
 537:     }
 538: 
 539:     public Keymap getResolveParent()
 540:     {
 541:       return parent;
 542:     }
 543: 
 544:     public boolean isLocallyDefined(KeyStroke key)
 545:     {
 546:       return map.containsKey(key);
 547:     }
 548: 
 549:     public void removeBindings()
 550:     {
 551:       map.clear();
 552:     }
 553: 
 554:     public void removeKeyStrokeBinding(KeyStroke key)
 555:     {
 556:       map.remove(key);
 557:     }
 558: 
 559:     public void setDefaultAction(Action a)
 560:     {
 561:       defaultAction = a;
 562:     }
 563: 
 564:     public void setResolveParent(Keymap p)
 565:     {
 566:       parent = p;
 567:     }
 568:   }
 569: 
 570:   class DefaultTransferHandler extends TransferHandler
 571:   {
 572:     public boolean canImport(JComponent component, DataFlavor[] flavors)
 573:     {
 574:       JTextComponent textComponent = (JTextComponent) component;
 575:       
 576:       if (! (textComponent.isEnabled()
 577:          && textComponent.isEditable()
 578:          && flavors != null))
 579:         return false;
 580: 
 581:       for (int i = 0; i < flavors.length; ++i)
 582:     if (flavors[i].equals(DataFlavor.stringFlavor))
 583:        return true;
 584: 
 585:       return false;
 586:     }
 587:     
 588:     public void exportToClipboard(JComponent component, Clipboard clipboard,
 589:                   int action)
 590:     {
 591:       JTextComponent textComponent = (JTextComponent) component;
 592:       int start = textComponent.getSelectionStart();
 593:       int end = textComponent.getSelectionEnd();
 594: 
 595:       if (start == end)
 596:         return;
 597: 
 598:       try
 599:         {
 600:           // Copy text to clipboard.
 601:           String data = textComponent.getDocument().getText(start, end);
 602:           StringSelection selection = new StringSelection(data);
 603:           clipboard.setContents(selection, null);
 604: 
 605:           // Delete selected text on cut action.
 606:           if (action == MOVE)
 607:             doc.remove(start, end - start);
 608:         }
 609:       catch (BadLocationException e)
 610:         {
 611:           // Ignore this and do nothing.
 612:         }
 613:     }
 614:     
 615:     public int getSourceActions()
 616:     {
 617:       return NONE;
 618:     }
 619: 
 620:     public boolean importData(JComponent component, Transferable transferable)
 621:     {
 622:       DataFlavor flavor = null;
 623:       DataFlavor[] flavors = transferable.getTransferDataFlavors();
 624: 
 625:       if (flavors == null)
 626:         return false;
 627: 
 628:       for (int i = 0; i < flavors.length; ++i)
 629:         if (flavors[i].equals(DataFlavor.stringFlavor))
 630:           flavor = flavors[i];
 631:       
 632:       if (flavor == null)
 633:         return false;
 634: 
 635:       try
 636:         {
 637:           JTextComponent textComponent = (JTextComponent) component;
 638:           String data = (String) transferable.getTransferData(flavor);
 639:           textComponent.replaceSelection(data);
 640:           return true;
 641:         }
 642:       catch (IOException e)
 643:         {
 644:           // Ignored.
 645:         }
 646:       catch (UnsupportedFlavorException e)
 647:         {
 648:           // Ignored.
 649:         }
 650: 
 651:       return false;
 652:     }
 653:   }
 654: 
 655:   private static final long serialVersionUID = -8796518220218978795L;
 656:   
 657:   public static final String DEFAULT_KEYMAP = "default";
 658:   public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
 659:   
 660:   private static DefaultTransferHandler defaultTransferHandler;
 661:   private static Hashtable keymaps = new Hashtable();
 662:   private Keymap keymap;
 663:   private char focusAccelerator = '\0';
 664:   private NavigationFilter navigationFilter;
 665: 
 666:   /**
 667:    * Get a Keymap from the global keymap table, by name.
 668:    *
 669:    * @param n The name of the Keymap to look up
 670:    *
 671:    * @return A Keymap associated with the provided name, or
 672:    * <code>null</code> if no such Keymap exists
 673:    *
 674:    * @see #addKeymap
 675:    * @see #removeKeymap
 676:    * @see #keymaps
 677:    */
 678:   public static Keymap getKeymap(String n)
 679:   {
 680:     return (Keymap) keymaps.get(n);
 681:   }
 682: 
 683:   /**
 684:    * Remove a Keymap from the global Keymap table, by name.
 685:    *
 686:    * @param n The name of the Keymap to remove
 687:    *
 688:    * @return The keymap removed from the global table
 689:    *
 690:    * @see #addKeymap
 691:    * @see #getKeymap()
 692:    * @see #keymaps
 693:    */  
 694:   public static Keymap removeKeymap(String n)
 695:   {
 696:     Keymap km = (Keymap) keymaps.get(n);
 697:     keymaps.remove(n);
 698:     return km;
 699:   }
 700: 
 701:   /**
 702:    * Create a new Keymap with a specific name and parent, and add the new
 703:    * Keymap to the global keymap table. The name may be <code>null</code>,
 704:    * in which case the new Keymap will <em>not</em> be added to the global
 705:    * Keymap table. The parent may also be <code>null</code>, which is
 706:    * harmless.
 707:    * 
 708:    * @param n The name of the new Keymap, or <code>null</code>
 709:    * @param parent The parent of the new Keymap, or <code>null</code>
 710:    *
 711:    * @return The newly created Keymap
 712:    *
 713:    * @see #removeKeymap
 714:    * @see #getKeymap()
 715:    * @see #keymaps
 716:    */
 717:   public static Keymap addKeymap(String n, Keymap parent)
 718:   {
 719:     Keymap k = new DefaultKeymap(n);
 720:     k.setResolveParent(parent);
 721:     if (n != null)
 722:       keymaps.put(n, k);
 723:     return k;
 724:   }
 725: 
 726:   /**
 727:    * Get the current Keymap of this component.
 728:    *
 729:    * @return The component's current Keymap
 730:    *
 731:    * @see #setKeymap
 732:    * @see #keymap
 733:    */
 734:   public Keymap getKeymap() 
 735:   {
 736:     return keymap;
 737:   }
 738: 
 739:   /**
 740:    * Set the current Keymap of this component, installing appropriate
 741:    * {@link KeymapWrapper} and {@link KeymapActionMap} objects in the
 742:    * {@link InputMap} and {@link ActionMap} parent chains, respectively,
 743:    * and fire a property change event with name <code>"keymap"</code>.
 744:    *
 745:    * @see #getKeymap()
 746:    * @see #keymap
 747:    */
 748:   public void setKeymap(Keymap k) 
 749:   {
 750: 
 751:     // phase 1: replace the KeymapWrapper entry in the InputMap chain.
 752:     // the goal here is to always maintain the following ordering:
 753:     //
 754:     //   [InputMap]? -> [KeymapWrapper]? -> [InputMapUIResource]*
 755:     // 
 756:     // that is to say, component-specific InputMaps need to remain children
 757:     // of Keymaps, and Keymaps need to remain children of UI-installed
 758:     // InputMaps (and the order of each group needs to be preserved, of
 759:     // course).
 760:     
 761:     KeymapWrapper kw = (k == null ? null : new KeymapWrapper(k));
 762:     InputMap childInputMap = getInputMap(JComponent.WHEN_FOCUSED);
 763:     if (childInputMap == null)
 764:       setInputMap(JComponent.WHEN_FOCUSED, kw);
 765:     else
 766:       {
 767:         while (childInputMap.getParent() != null 
 768:                && !(childInputMap.getParent() instanceof KeymapWrapper)
 769:                && !(childInputMap.getParent() instanceof InputMapUIResource))
 770:           childInputMap = childInputMap.getParent();
 771: 
 772:         // option 1: there is nobody to replace at the end of the chain
 773:         if (childInputMap.getParent() == null)
 774:           childInputMap.setParent(kw);
 775:         
 776:         // option 2: there is already a KeymapWrapper in the chain which
 777:         // needs replacing (possibly with its own parents, possibly without)
 778:         else if (childInputMap.getParent() instanceof KeymapWrapper)
 779:           {
 780:             if (kw == null)
 781:               childInputMap.setParent(childInputMap.getParent().getParent());
 782:             else
 783:               {
 784:                 kw.setParent(childInputMap.getParent().getParent());
 785:                 childInputMap.setParent(kw);
 786:               }
 787:           }
 788: 
 789:         // option 3: there is an InputMapUIResource in the chain, which marks
 790:         // the place where we need to stop and insert ourselves
 791:         else if (childInputMap.getParent() instanceof InputMapUIResource)
 792:           {
 793:             if (kw != null)
 794:               {
 795:                 kw.setParent(childInputMap.getParent());
 796:                 childInputMap.setParent(kw);
 797:               }
 798:           }
 799:       }
 800: 
 801:     // phase 2: replace the KeymapActionMap entry in the ActionMap chain
 802: 
 803:     KeymapActionMap kam = (k == null ? null : new KeymapActionMap(k));
 804:     ActionMap childActionMap = getActionMap();
 805:     if (childActionMap == null)
 806:       setActionMap(kam);
 807:     else
 808:       {
 809:         while (childActionMap.getParent() != null 
 810:                && !(childActionMap.getParent() instanceof KeymapActionMap)
 811:                && !(childActionMap.getParent() instanceof ActionMapUIResource))
 812:           childActionMap = childActionMap.getParent();
 813: 
 814:         // option 1: there is nobody to replace at the end of the chain
 815:         if (childActionMap.getParent() == null)
 816:           childActionMap.setParent(kam);
 817:         
 818:         // option 2: there is already a KeymapActionMap in the chain which
 819:         // needs replacing (possibly with its own parents, possibly without)
 820:         else if (childActionMap.getParent() instanceof KeymapActionMap)
 821:           {
 822:             if (kam == null)
 823:               childActionMap.setParent(childActionMap.getParent().getParent());
 824:             else
 825:               {
 826:                 kam.setParent(childActionMap.getParent().getParent());
 827:                 childActionMap.setParent(kam);
 828:               }
 829:           }
 830: 
 831:         // option 3: there is an ActionMapUIResource in the chain, which marks
 832:         // the place where we need to stop and insert ourselves
 833:         else if (childActionMap.getParent() instanceof ActionMapUIResource)
 834:           {
 835:             if (kam != null)
 836:               {
 837:                 kam.setParent(childActionMap.getParent());
 838:                 childActionMap.setParent(kam);
 839:               }
 840:           }
 841:       }
 842: 
 843:     // phase 3: update the explicit keymap field
 844: 
 845:     Keymap old = keymap;
 846:     keymap = k;
 847:     firePropertyChange("keymap", old, k);
 848:   }
 849: 
 850:   /**
 851:    * Resolves a set of bindings against a set of actions and inserts the
 852:    * results into a {@link Keymap}. Specifically, for each provided binding
 853:    * <code>b</code>, if there exists a provided action <code>a</code> such
 854:    * that <code>a.getValue(Action.NAME) == b.ActionName</code> then an
 855:    * entry is added to the Keymap mapping <code>b</code> to
 856:    * <code>a</code>.
 857:    *
 858:    * @param map The Keymap to add new mappings to
 859:    * @param bindings The set of bindings to add to the Keymap
 860:    * @param actions The set of actions to resolve binding names against
 861:    *
 862:    * @see Action#NAME
 863:    * @see Action#getValue
 864:    * @see KeyBinding#actionName
 865:    */
 866:   public static void loadKeymap(Keymap map, 
 867:                                 JTextComponent.KeyBinding[] bindings, 
 868:                                 Action[] actions)
 869:   {
 870:     Hashtable acts = new Hashtable(actions.length);
 871:     for (int i = 0; i < actions.length; ++i)
 872:       acts.put(actions[i].getValue(Action.NAME), actions[i]);
 873:       for (int i = 0; i < bindings.length; ++i)
 874:       if (acts.containsKey(bindings[i].actionName))
 875:         map.addActionForKeyStroke(bindings[i].key, (Action) acts.get(bindings[i].actionName));
 876:   }
 877: 
 878:   /**
 879:    * Returns the set of available Actions this component's associated
 880:    * editor can run.  Equivalent to calling
 881:    * <code>getUI().getEditorKit().getActions()</code>. This set of Actions
 882:    * is a reasonable value to provide as a parameter to {@link
 883:    * #loadKeymap}, when resolving a set of {@link KeyBinding} objects
 884:    * against this component.
 885:    *
 886:    * @return The set of available Actions on this component's {@link EditorKit}
 887:    *
 888:    * @see TextUI#getEditorKit
 889:    * @see EditorKit#getActions()
 890:    */
 891:   public Action[] getActions()
 892:   {
 893:     return getUI().getEditorKit(this).getActions();
 894:   }
 895:     
 896:   // These are package-private to avoid an accessor method.
 897:   Document doc;
 898:   Caret caret;
 899:   boolean editable;
 900:   
 901:   private Highlighter highlighter;
 902:   private Color caretColor;
 903:   private Color disabledTextColor;
 904:   private Color selectedTextColor;
 905:   private Color selectionColor;
 906:   private Insets margin;
 907:   private boolean dragEnabled;
 908: 
 909:   /**
 910:    * Creates a new <code>JTextComponent</code> instance.
 911:    */
 912:   public JTextComponent()
 913:   {
 914:     Keymap defkeymap = getKeymap(DEFAULT_KEYMAP);
 915:     if (defkeymap == null)
 916:       {
 917:         defkeymap = addKeymap(DEFAULT_KEYMAP, null);
 918:         defkeymap.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction());
 919:       }
 920: 
 921:     setFocusable(true);
 922:     setEditable(true);
 923:     enableEvents(AWTEvent.KEY_EVENT_MASK);
 924:     updateUI();
 925:   }
 926: 
 927:   public void setDocument(Document newDoc)
 928:   {
 929:     Document oldDoc = doc;
 930:     doc = newDoc;
 931:     firePropertyChange("document", oldDoc, newDoc);
 932:     revalidate();
 933:     repaint();
 934:   }
 935: 
 936:   public Document getDocument()
 937:   {
 938:     return doc;
 939:   }
 940: 
 941:   /**
 942:    * Get the <code>AccessibleContext</code> of this object.
 943:    *
 944:    * @return an <code>AccessibleContext</code> object
 945:    */
 946:   public AccessibleContext getAccessibleContext()
 947:   {
 948:     return null;
 949:   }
 950: 
 951:   public void setMargin(Insets m)
 952:   {
 953:     margin = m;
 954:   }
 955: 
 956:   public Insets getMargin()
 957:   {
 958:     return margin;
 959:   }
 960: 
 961:   public void setText(String text)
 962:   {
 963:     try
 964:       {
 965:         if (doc instanceof AbstractDocument)
 966:           ((AbstractDocument) doc).replace(0, doc.getLength(), text, null);
 967:         else
 968:           {
 969:             doc.remove(0, doc.getLength());
 970:             doc.insertString(0, text, null);
 971:           }
 972:       }
 973:     catch (BadLocationException e)
 974:       {
 975:         // This can never happen.
 976:       }
 977:   }
 978: 
 979:   /**
 980:    * Retrieves the current text in this text document.
 981:    *
 982:    * @return the text
 983:    *
 984:    * @exception NullPointerException if the underlaying document is null
 985:    */
 986:   public String getText()
 987:   {
 988:     if (doc == null)
 989:       return null;
 990: 
 991:     try
 992:       {
 993:         return doc.getText(0, doc.getLength());
 994:       }
 995:     catch (BadLocationException e)
 996:       {
 997:         // This should never happen.
 998:         return "";
 999:       }
1000:   }
1001: 
1002:   /**
1003:    * Retrieves a part of the current text in this document.
1004:    *
1005:    * @param offset the postion of the first character
1006:    * @param length the length of the text to retrieve
1007:    *
1008:    * @return the text
1009:    *
1010:    * @exception BadLocationException if arguments do not hold pre-conditions
1011:    */
1012:   public String getText(int offset, int length)
1013:     throws BadLocationException
1014:   {
1015:     return getDocument().getText(offset, length);
1016:   }
1017: 
1018:   /**
1019:    * Retrieves the currently selected text in this text document.
1020:    *
1021:    * @return the selected text
1022:    *
1023:    * @exception NullPointerException if the underlaying document is null
1024:    */
1025:   public String getSelectedText()
1026:   {
1027:     try
1028:       {
1029:         return doc.getText(getSelectionStart(), getSelectionEnd());
1030:       }
1031:     catch (BadLocationException e)
1032:       {
1033:         // This should never happen.
1034:         return null;
1035:       }
1036:   }
1037: 
1038:   /**
1039:    * Returns a string that specifies the name of the Look and Feel class
1040:    * that renders this component.
1041:    *
1042:    * @return the string "TextComponentUI"
1043:    */
1044:   public String getUIClassID()
1045:   {
1046:     return "TextComponentUI";
1047:   }
1048: 
1049:   /**
1050:    * Returns a string representation of this JTextComponent.
1051:    */
1052:   protected String paramString()
1053:   {
1054:     // TODO: Do something useful here.
1055:     return super.paramString();
1056:   }
1057: 
1058:   /**
1059:    * This method returns the label's UI delegate.
1060:    *
1061:    * @return The label's UI delegate.
1062:    */
1063:   public TextUI getUI()
1064:   {
1065:     return (TextUI) ui;
1066:   }
1067: 
1068:   /**
1069:    * This method sets the label's UI delegate.
1070:    *
1071:    * @param newUI The label's UI delegate.
1072:    */
1073:   public void setUI(TextUI newUI)
1074:   {
1075:     super.setUI(newUI);
1076:   }
1077: 
1078:   /**
1079:    * This method resets the label's UI delegate to the default UI for the
1080:    * current look and feel.
1081:    */
1082:   public void updateUI()
1083:   {
1084:     setUI((TextUI) UIManager.getUI(this));
1085:   }
1086: 
1087:   public Dimension getPreferredScrollableViewportSize()
1088:   {
1089:     return getPreferredSize();
1090:   }
1091: 
1092:   public int getScrollableUnitIncrement(Rectangle visible, int orientation,
1093:                                         int direction)
1094:   {
1095:     // We return 1/10 of the visible area as documented in Sun's API docs.
1096:     if (orientation == SwingConstants.HORIZONTAL)
1097:       return visible.width / 10;
1098:     else if (orientation == SwingConstants.VERTICAL)
1099:       return visible.height / 10;
1100:     else
1101:       throw new IllegalArgumentException("orientation must be either "
1102:                                       + "javax.swing.SwingConstants.VERTICAL "
1103:                                       + "or "
1104:                                       + "javax.swing.SwingConstants.HORIZONTAL"
1105:                                          );
1106:   }
1107: 
1108:   public int getScrollableBlockIncrement(Rectangle visible, int orientation,
1109:                                          int direction)
1110:   {
1111:     // We return the whole visible area as documented in Sun's API docs.
1112:     if (orientation == SwingConstants.HORIZONTAL)
1113:       return visible.width;
1114:     else if (orientation == SwingConstants.VERTICAL)
1115:       return visible.height;
1116:     else
1117:       throw new IllegalArgumentException("orientation must be either "
1118:                                       + "javax.swing.SwingConstants.VERTICAL "
1119:                                       + "or "
1120:                                       + "javax.swing.SwingConstants.HORIZONTAL"
1121:                                          );
1122:   }
1123: 
1124:   /**
1125:    * Checks whether this text component it editable.
1126:    *
1127:    * @return true if editable, false otherwise
1128:    */
1129:   public boolean isEditable()
1130:   {
1131:     return editable;
1132:   }
1133: 
1134:   /**
1135:    * Enables/disabled this text component's editability.
1136:    *
1137:    * @param newValue true to make it editable, false otherwise.
1138:    */
1139:   public void setEditable(boolean newValue)
1140:   {
1141:     if (editable == newValue)
1142:       return;
1143: 
1144:     boolean oldValue = editable;
1145:     editable = newValue;
1146:     firePropertyChange("editable", oldValue, newValue);
1147:   }
1148: 
1149:   /**
1150:    * The <code>Caret</code> object used in this text component.
1151:    *
1152:    * @return the caret object
1153:    */
1154:   public Caret getCaret()
1155:   {
1156:     return caret;
1157:   }
1158: 
1159:   /**
1160:    * Sets a new <code>Caret</code> for this text component.
1161:    *
1162:    * @param newCaret the new <code>Caret</code> to set
1163:    */
1164:   public void setCaret(Caret newCaret)
1165:   {
1166:     if (caret != null)
1167:       caret.deinstall(this);
1168:     
1169:     Caret oldCaret = caret;
1170:     caret = newCaret;
1171: 
1172:     if (caret != null)
1173:       caret.install(this);
1174:     
1175:     firePropertyChange("caret", oldCaret, newCaret);
1176:   }
1177: 
1178:   public Color getCaretColor()
1179:   {
1180:     return caretColor;
1181:   }
1182: 
1183:   public void setCaretColor(Color newColor)
1184:   {
1185:     Color oldCaretColor = caretColor;
1186:     caretColor = newColor;
1187:     firePropertyChange("caretColor", oldCaretColor, newColor);
1188:   }
1189: 
1190:   public Color getDisabledTextColor()
1191:   {
1192:     return disabledTextColor;
1193:   }
1194: 
1195:   public void setDisabledTextColor(Color newColor)
1196:   {
1197:     Color oldColor = disabledTextColor;
1198:     disabledTextColor = newColor;
1199:     firePropertyChange("disabledTextColor", oldColor, newColor);
1200:   }
1201: 
1202:   public Color getSelectedTextColor()
1203:   {
1204:     return selectedTextColor;
1205:   }
1206: 
1207:   public void setSelectedTextColor(Color newColor)
1208:   {
1209:     Color oldColor = selectedTextColor;
1210:     selectedTextColor = newColor;
1211:     firePropertyChange("selectedTextColor", oldColor, newColor);
1212:   }
1213: 
1214:   public Color getSelectionColor()
1215:   {
1216:     return selectionColor;
1217:   }
1218: 
1219:   public void setSelectionColor(Color newColor)
1220:   {
1221:     Color oldColor = selectionColor;
1222:     selectionColor = newColor;
1223:     firePropertyChange("selectionColor", oldColor, newColor);
1224:   }
1225: 
1226:   /**
1227:    * Retrisves the current caret position.
1228:    *
1229:    * @return the current position
1230:    */
1231:   public int getCaretPosition()
1232:   {
1233:     return caret.getDot();
1234:   }
1235: 
1236:   /**
1237:    * Sets the caret to a new position.
1238:    *
1239:    * @param position the new position
1240:    */
1241:   public void setCaretPosition(int position)
1242:   {
1243:     if (doc == null)
1244:       return;
1245: 
1246:     if (position < 0 || position > doc.getLength())
1247:       throw new IllegalArgumentException();
1248: 
1249:     caret.setDot(position);
1250:   }
1251: 
1252:   /**
1253:    * Moves the caret to a given position. This selects the text between
1254:    * the old and the new position of the caret.
1255:    */
1256:   public void moveCaretPosition(int position)
1257:   {
1258:     if (doc == null)
1259:       return;
1260: 
1261:     if (position < 0 || position > doc.getLength())
1262:       throw new IllegalArgumentException();
1263: 
1264:     caret.moveDot(position);
1265:   }
1266: 
1267:   public Highlighter getHighlighter()
1268:   {
1269:     return highlighter;
1270:   }
1271: 
1272:   public void setHighlighter(Highlighter newHighlighter)
1273:   {
1274:     if (highlighter != null)
1275:       highlighter.deinstall(this);
1276:     
1277:     Highlighter oldHighlighter = highlighter;
1278:     highlighter = newHighlighter;
1279: 
1280:     if (highlighter != null)
1281:       highlighter.install(this);
1282:     
1283:     firePropertyChange("highlighter", oldHighlighter, newHighlighter);
1284:   }
1285: 
1286:   /**
1287:    * Returns the start postion of the currently selected text.
1288:    *
1289:    * @return the start postion
1290:    */
1291:   public int getSelectionStart()
1292:   {
1293:     return Math.min(caret.getDot(), caret.getMark());
1294:   }
1295: 
1296:   /**
1297:    * Selects the text from the given postion to the selection end position.
1298:    *
1299:    * @param start the start positon of the selected text.
1300:    */
1301:   public void setSelectionStart(int start)
1302:   {
1303:     select(start, getSelectionEnd());
1304:   }
1305: 
1306:   /**
1307:    * Returns the end postion of the currently selected text.
1308:    *
1309:    * @return the end postion
1310:    */
1311:   public int getSelectionEnd()
1312:   {
1313:     return Math.max(caret.getDot(), caret.getMark());
1314:   }
1315: 
1316:   /**
1317:    * Selects the text from the selection start postion to the given position.
1318:    *
1319:    * @param end the end positon of the selected text.
1320:    */
1321:   public void setSelectionEnd(int end)
1322:   {
1323:     select(getSelectionStart(), end);
1324:   }
1325: 
1326:   /**
1327:    * Selects a part of the content of the text component.
1328:    *
1329:    * @param start the start position of the selected text
1330:    * @param end the end position of the selected text
1331:    */
1332:   public void select(int start, int end)
1333:   {
1334:     int length = doc.getLength();
1335:     
1336:     start = Math.max(start, 0);
1337:     start = Math.min(start, length);
1338: 
1339:     end = Math.max(end, start);
1340:     end = Math.min(end, length);
1341: 
1342:     setCaretPosition(start);
1343:     moveCaretPosition(end);
1344:   }
1345: 
1346:   /**
1347:    * Selects the whole content of the text component.
1348:    */
1349:   public void selectAll()
1350:   {
1351:     select(0, doc.getLength());
1352:   }
1353: 
1354:   public synchronized void replaceSelection(String content)
1355:   {
1356:     int dot = caret.getDot();
1357:     int mark = caret.getMark();
1358: 
1359:     // If content is empty delete selection.
1360:     if (content == null)
1361:       {
1362:         caret.setDot(dot);
1363:         return;
1364:       }
1365: 
1366:     try
1367:       {
1368:         int start = getSelectionStart();
1369:         int end = getSelectionEnd();
1370: 
1371:         // Remove selected text.
1372:         if (dot != mark)
1373:           doc.remove(start, end - start);
1374: 
1375:         // Insert new text.
1376:         doc.insertString(start, content, null);
1377: 
1378:         // Set dot to new position.
1379:         setCaretPosition(start + content.length());
1380:       }
1381:     catch (BadLocationException e)
1382:       {
1383:         // This should never happen.
1384:       }
1385:   }
1386: 
1387:   public boolean getScrollableTracksViewportHeight()
1388:   {
1389:     if (getParent() instanceof JViewport)
1390:       return ((JViewport) getParent()).getHeight() > getPreferredSize().height;
1391: 
1392:     return false;
1393:   }
1394: 
1395:   public boolean getScrollableTracksViewportWidth()
1396:   {
1397:     if (getParent() instanceof JViewport)
1398:       return ((JViewport) getParent()).getWidth() > getPreferredSize().width;
1399: 
1400:     return false;
1401:   }
1402: 
1403:   /**
1404:    * Adds a <code>CaretListener</code> object to this text component.
1405:    *
1406:    * @param listener the listener to add
1407:    */
1408:   public void addCaretListener(CaretListener listener)
1409:   {
1410:     listenerList.add(CaretListener.class, listener);
1411:   }
1412: 
1413:   /**
1414:    * Removed a <code>CaretListener</code> object from this text component.
1415:    *
1416:    * @param listener the listener to remove
1417:    */
1418:   public void removeCaretListener(CaretListener listener)
1419:   {
1420:     listenerList.remove(CaretListener.class, listener);
1421:   }
1422: 
1423:   /**
1424:    * Returns all added <code>CaretListener</code> objects.
1425:    *
1426:    * @return an array of listeners
1427:    */
1428:   public CaretListener[] getCaretListeners()
1429:   {
1430:     return (CaretListener[]) getListeners(CaretListener.class);
1431:   }
1432: 
1433:   /**
1434:    * Notifies all registered <code>CaretListener</code> objects that the caret
1435:    * was updated.
1436:    *
1437:    * @param event the event to send
1438:    */
1439:   protected void fireCaretUpdate(CaretEvent event)
1440:   {
1441:     CaretListener[] listeners = getCaretListeners();
1442: 
1443:     for (int index = 0; index < listeners.length; ++index)
1444:       listeners[index].caretUpdate(event);
1445:   }
1446: 
1447:   /**
1448:    * Adds an <code>InputListener</code> object to this text component.
1449:    *
1450:    * @param listener the listener to add
1451:    */
1452:   public void addInputMethodListener(InputMethodListener listener)
1453:   {
1454:     listenerList.add(InputMethodListener.class, listener);
1455:   }
1456: 
1457:   /**
1458:    * Removes an <code>InputListener</code> object from this text component.
1459:    *
1460:    * @param listener the listener to remove
1461:    */
1462:   public void removeInputMethodListener(InputMethodListener listener)
1463:   {
1464:     listenerList.remove(InputMethodListener.class, listener);
1465:   }
1466: 
1467:   /**
1468:    * Returns all added <code>InputMethodListener</code> objects.
1469:    *
1470:    * @return an array of listeners
1471:    */
1472:   public InputMethodListener[] getInputMethodListeners()
1473:   {
1474:     return (InputMethodListener[]) getListeners(InputMethodListener.class);
1475:   }
1476: 
1477:   public Rectangle modelToView(int position) throws BadLocationException
1478:   {
1479:     return getUI().modelToView(this, position);
1480:   }
1481: 
1482:   public boolean getDragEnabled()
1483:   {
1484:     return dragEnabled;
1485:   }
1486: 
1487:   public void setDragEnabled(boolean enabled)
1488:   {
1489:     dragEnabled = enabled;
1490:   }
1491: 
1492:   public int viewToModel(Point pt)
1493:   {
1494:     return getUI().viewToModel(this, pt);
1495:   }
1496: 
1497:   public void copy()
1498:   {
1499:     doTransferAction("copy", TransferHandler.getCopyAction());
1500:   }
1501: 
1502:   public void cut()
1503:   {
1504:     doTransferAction("cut", TransferHandler.getCutAction());
1505:   }
1506: 
1507:   public void paste()
1508:   {
1509:     doTransferAction("paste", TransferHandler.getPasteAction());
1510:   }
1511: 
1512:   private void doTransferAction(String name, Action action)
1513:   {
1514:     // Install default TransferHandler if none set.
1515:     if (getTransferHandler() == null)
1516:       {
1517:         if (defaultTransferHandler == null)
1518:           defaultTransferHandler = new DefaultTransferHandler();
1519: 
1520:         setTransferHandler(defaultTransferHandler);
1521:       }
1522: 
1523:     // Perform action.
1524:     ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
1525:                                         action.getValue(Action.NAME).toString());
1526:     action.actionPerformed(event);
1527:   }
1528: 
1529:   public void setFocusAccelerator(char newKey)
1530:   {
1531:     if (focusAccelerator == newKey)
1532:       return;
1533: 
1534:     char oldKey = focusAccelerator;
1535:     focusAccelerator = newKey;
1536:     firePropertyChange(FOCUS_ACCELERATOR_KEY, oldKey, newKey);
1537:   }
1538:   
1539:   public char getFocusAccelerator()
1540:   {
1541:     return focusAccelerator;
1542:   }
1543: 
1544:   /**
1545:    * @since 1.4
1546:    */
1547:   public NavigationFilter getNavigationFilter()
1548:   {
1549:     return navigationFilter;
1550:   }
1551: 
1552:   /**
1553:    * @since 1.4
1554:    */
1555:   public void setNavigationFilter(NavigationFilter filter)
1556:   {
1557:     navigationFilter = filter;
1558:   }
1559:   
1560:   /**
1561:    * Read and set the content this component. If not overridden, the
1562:    * method reads the component content as a plain text.
1563:    *
1564:    * The second parameter of this method describes the input stream. It can
1565:    * be String, URL, File and so on. If not null, this object is added to
1566:    * the properties of the associated document under the key
1567:    * {@link Document#StreamDescriptionProperty}.
1568:    *
1569:    * @param input an input stream to read from.
1570:    * @param streamDescription an object, describing the stream.
1571:    *
1572:    * @throws IOException if the reader throws it.
1573:    *
1574:    * @see #getDocument()
1575:    * @see Document#getProperty(Object)
1576:    */
1577:   public void read(Reader input, Object streamDescription)
1578:             throws IOException
1579:   {
1580:     if (streamDescription != null)
1581:       {
1582:         Document d = getDocument();
1583:         if (d != null)
1584:           d.putProperty(Document.StreamDescriptionProperty, streamDescription);
1585:       }
1586: 
1587:     StringBuffer b = new StringBuffer();
1588:     int c;
1589: 
1590:     // Read till -1 (EOF).
1591:     while ((c = input.read()) >= 0)
1592:       b.append((char) c);
1593: 
1594:     setText(b.toString());
1595:   }
1596: 
1597:   /**
1598:    * Write the content of this component to the given stream. If not
1599:    * overridden, the method writes the component content as a plain text.
1600:    *
1601:    * @param output the writer to write into.
1602:    *
1603:    * @throws IOException if the writer throws it.
1604:    */
1605:   public void write(Writer output)
1606:              throws IOException
1607:   {
1608:     output.write(getText());
1609:   }
1610: 
1611:   /**
1612:    * Returns the tooltip text for this text component for the given mouse
1613:    * event. This forwards the call to
1614:    * {@link TextUI#getToolTipText(JTextComponent, Point)}.
1615:    *
1616:    * @param ev the mouse event
1617:    *
1618:    * @return the tooltip text for this text component for the given mouse
1619:    *         event
1620:    */
1621:   public String getToolTipText(MouseEvent ev)
1622:   {
1623:     return getUI().getToolTipText(this, ev.getPoint());
1624:   }
1625: }