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