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