Source for javax.swing.text.JTextComponent

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