1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing.text;
  26 
  27 import com.sun.beans.util.Cache;
  28 
  29 import java.security.AccessController;
  30 import java.security.PrivilegedAction;
  31 
  32 import java.beans.JavaBean;
  33 import java.beans.BeanProperty;
  34 import java.beans.Transient;
  35 import java.util.HashMap;
  36 import java.util.Hashtable;
  37 import java.util.Enumeration;
  38 import java.util.Vector;
  39 
  40 import java.util.concurrent.*;
  41 
  42 import java.io.*;
  43 
  44 import java.awt.*;
  45 import java.awt.event.*;
  46 import java.awt.print.*;
  47 import java.awt.datatransfer.*;
  48 import java.awt.im.InputContext;
  49 import java.awt.im.InputMethodRequests;
  50 import java.awt.font.TextHitInfo;
  51 import java.awt.font.TextAttribute;
  52 
  53 import java.awt.print.Printable;
  54 import java.awt.print.PrinterException;
  55 
  56 import javax.print.PrintService;
  57 import javax.print.attribute.PrintRequestAttributeSet;
  58 
  59 import java.text.*;
  60 import java.text.AttributedCharacterIterator.Attribute;
  61 
  62 import javax.swing.*;
  63 import javax.swing.event.*;
  64 import javax.swing.plaf.*;
  65 
  66 import javax.accessibility.*;
  67 
  68 import javax.print.attribute.*;
  69 
  70 import sun.awt.AppContext;
  71 
  72 import sun.swing.PrintingStatus;
  73 import sun.swing.SwingUtilities2;
  74 import sun.swing.text.TextComponentPrintable;
  75 import sun.swing.SwingAccessor;
  76 
  77 /**
  78  * <code>JTextComponent</code> is the base class for swing text
  79  * components.  It tries to be compatible with the
  80  * <code>java.awt.TextComponent</code> class
  81  * where it can reasonably do so.  Also provided are other services
  82  * for additional flexibility (beyond the pluggable UI and bean
  83  * support).
  84  * You can find information on how to use the functionality
  85  * this class provides in
  86  * <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/generaltext.html">General Rules for Using Text Components</a>,
  87  * a section in <em>The Java Tutorial.</em>
  88  *
  89  * <dl>
  90  * <dt><b>Caret Changes</b>
  91  * <dd>
  92  * The caret is a pluggable object in swing text components.
  93  * Notification of changes to the caret position and the selection
  94  * are sent to implementations of the <code>CaretListener</code>
  95  * interface that have been registered with the text component.
  96  * The UI will install a default caret unless a customized caret
  97  * has been set. <br>
  98  * By default the caret tracks all the document changes
  99  * performed on the Event Dispatching Thread and updates it's position
 100  * accordingly if an insertion occurs before or at the caret position
 101  * or a removal occurs before the caret position. <code>DefaultCaret</code>
 102  * tries to make itself visible which may lead to scrolling
 103  * of a text component within <code>JScrollPane</code>. The default caret
 104  * behavior can be changed by the {@link DefaultCaret#setUpdatePolicy} method.
 105  * <br>
 106  * <b>Note</b>: Non-editable text components also have a caret though
 107  * it may not be painted.
 108  *
 109  * <dt><b>Commands</b>
 110  * <dd>
 111  * Text components provide a number of commands that can be used
 112  * to manipulate the component.  This is essentially the way that
 113  * the component expresses its capabilities.  These are expressed
 114  * in terms of the swing <code>Action</code> interface,
 115  * using the <code>TextAction</code> implementation.
 116  * The set of commands supported by the text component can be
 117  * found with the {@link #getActions} method.  These actions
 118  * can be bound to key events, fired from buttons, etc.
 119  *
 120  * <dt><b>Text Input</b>
 121  * <dd>
 122  * The text components support flexible and internationalized text input, using
 123  * keymaps and the input method framework, while maintaining compatibility with
 124  * the AWT listener model.
 125  * <p>
 126  * A {@link javax.swing.text.Keymap} lets an application bind key
 127  * strokes to actions.
 128  * In order to allow keymaps to be shared across multiple text components, they
 129  * can use actions that extend <code>TextAction</code>.
 130  * <code>TextAction</code> can determine which <code>JTextComponent</code>
 131  * most recently has or had focus and therefore is the subject of
 132  * the action (In the case that the <code>ActionEvent</code>
 133  * sent to the action doesn't contain the target text component as its source).
 134  * <p>
 135  * The <a href="../../../../technotes/guides/imf/spec.html">input method framework</a>
 136  * lets text components interact with input methods, separate software
 137  * components that preprocess events to let users enter thousands of
 138  * different characters using keyboards with far fewer keys.
 139  * <code>JTextComponent</code> is an <em>active client</em> of
 140  * the framework, so it implements the preferred user interface for interacting
 141  * with input methods. As a consequence, some key events do not reach the text
 142  * component because they are handled by an input method, and some text input
 143  * reaches the text component as committed text within an {@link
 144  * java.awt.event.InputMethodEvent} instead of as a key event.
 145  * The complete text input is the combination of the characters in
 146  * <code>keyTyped</code> key events and committed text in input method events.
 147  * <p>
 148  * The AWT listener model lets applications attach event listeners to
 149  * components in order to bind events to actions. Swing encourages the
 150  * use of keymaps instead of listeners, but maintains compatibility
 151  * with listeners by giving the listeners a chance to steal an event
 152  * by consuming it.
 153  * <p>
 154  * Keyboard event and input method events are handled in the following stages,
 155  * with each stage capable of consuming the event:
 156  *
 157  * <table border=1 summary="Stages of keyboard and input method event handling">
 158  * <tr>
 159  * <th id="stage"><p style="text-align:left">Stage</p></th>
 160  * <th id="ke"><p style="text-align:left">KeyEvent</p></th>
 161  * <th id="ime"><p style="text-align:left">InputMethodEvent</p></th></tr>
 162  * <tr><td headers="stage">1.   </td>
 163  *     <td headers="ke">input methods </td>
 164  *     <td headers="ime">(generated here)</td></tr>
 165  * <tr><td headers="stage">2.   </td>
 166  *     <td headers="ke">focus manager </td>
 167  *     <td headers="ime"></td>
 168  * </tr>
 169  * <tr>
 170  *     <td headers="stage">3.   </td>
 171  *     <td headers="ke">registered key listeners</td>
 172  *     <td headers="ime">registered input method listeners</tr>
 173  * <tr>
 174  *     <td headers="stage">4.   </td>
 175  *     <td headers="ke"></td>
 176  *     <td headers="ime">input method handling in JTextComponent</tr>
 177  * <tr>
 178  *     <td headers="stage">5.   </td><td headers="ke ime" colspan=2>keymap handling using the current keymap</td></tr>
 179  * <tr><td headers="stage">6.   </td><td headers="ke">keyboard handling in JComponent (e.g. accelerators, component navigation, etc.)</td>
 180  *     <td headers="ime"></td></tr>
 181  * </table>
 182  *
 183  * <p>
 184  * To maintain compatibility with applications that listen to key
 185  * events but are not aware of input method events, the input
 186  * method handling in stage 4 provides a compatibility mode for
 187  * components that do not process input method events. For these
 188  * components, the committed text is converted to keyTyped key events
 189  * and processed in the key event pipeline starting at stage 3
 190  * instead of in the input method event pipeline.
 191  * <p>
 192  * By default the component will create a keymap (named <b>DEFAULT_KEYMAP</b>)
 193  * that is shared by all JTextComponent instances as the default keymap.
 194  * Typically a look-and-feel implementation will install a different keymap
 195  * that resolves to the default keymap for those bindings not found in the
 196  * different keymap. The minimal bindings include:
 197  * <ul>
 198  * <li>inserting content into the editor for the
 199  *  printable keys.
 200  * <li>removing content with the backspace and del
 201  *  keys.
 202  * <li>caret movement forward and backward
 203  * </ul>
 204  *
 205  * <dt><b>Model/View Split</b>
 206  * <dd>
 207  * The text components have a model-view split.  A text component pulls
 208  * together the objects used to represent the model, view, and controller.
 209  * The text document model may be shared by other views which act as observers
 210  * of the model (e.g. a document may be shared by multiple components).
 211  *
 212  * <p style="text-align:center"><img src="doc-files/editor.gif" alt="Diagram showing interaction between Controller, Document, events, and ViewFactory"
 213  *                  HEIGHT=358 WIDTH=587></p>
 214  *
 215  * <p>
 216  * The model is defined by the {@link Document} interface.
 217  * This is intended to provide a flexible text storage mechanism
 218  * that tracks change during edits and can be extended to more sophisticated
 219  * models.  The model interfaces are meant to capture the capabilities of
 220  * expression given by SGML, a system used to express a wide variety of
 221  * content.
 222  * Each modification to the document causes notification of the
 223  * details of the change to be sent to all observers in the form of a
 224  * {@link DocumentEvent} which allows the views to stay up to date with the model.
 225  * This event is sent to observers that have implemented the
 226  * {@link DocumentListener}
 227  * interface and registered interest with the model being observed.
 228  *
 229  * <dt><b>Location Information</b>
 230  * <dd>
 231  * The capability of determining the location of text in
 232  * the view is provided.  There are two methods, {@link #modelToView}
 233  * and {@link #viewToModel} for determining this information.
 234  *
 235  * <dt><b>Undo/Redo support</b>
 236  * <dd>
 237  * Support for an edit history mechanism is provided to allow
 238  * undo/redo operations.  The text component does not itself
 239  * provide the history buffer by default, but does provide
 240  * the <code>UndoableEdit</code> records that can be used in conjunction
 241  * with a history buffer to provide the undo/redo support.
 242  * The support is provided by the Document model, which allows
 243  * one to attach UndoableEditListener implementations.
 244  *
 245  * <dt><b>Thread Safety</b>
 246  * <dd>
 247  * The swing text components provide some support of thread
 248  * safe operations.  Because of the high level of configurability
 249  * of the text components, it is possible to circumvent the
 250  * protection provided.  The protection primarily comes from
 251  * the model, so the documentation of <code>AbstractDocument</code>
 252  * describes the assumptions of the protection provided.
 253  * The methods that are safe to call asynchronously are marked
 254  * with comments.
 255  *
 256  * <dt><b>Newlines</b>
 257  * <dd>
 258  * For a discussion on how newlines are handled, see
 259  * <a href="DefaultEditorKit.html">DefaultEditorKit</a>.
 260  *
 261  *
 262  * <dt><b>Printing support</b>
 263  * <dd>
 264  * Several {@link #print print} methods are provided for basic
 265  * document printing.  If more advanced printing is needed, use the
 266  * {@link #getPrintable} method.
 267  * </dl>
 268  *
 269  * <p>
 270  * <strong>Warning:</strong>
 271  * Serialized objects of this class will not be compatible with
 272  * future Swing releases. The current serialization support is
 273  * appropriate for short term storage or RMI between applications running
 274  * the same version of Swing.  As of 1.4, support for long term storage
 275  * of all JavaBeans&trade;
 276  * has been added to the <code>java.beans</code> package.
 277  * Please see {@link java.beans.XMLEncoder}.
 278  *
 279  * @author  Timothy Prinzing
 280  * @author Igor Kushnirskiy (printing support)
 281  * @see Document
 282  * @see DocumentEvent
 283  * @see DocumentListener
 284  * @see Caret
 285  * @see CaretEvent
 286  * @see CaretListener
 287  * @see TextUI
 288  * @see View
 289  * @see ViewFactory
 290  */
 291 @JavaBean(defaultProperty = "UI")
 292 @SwingContainer(false)
 293 @SuppressWarnings("serial") // Same-version serialization only
 294 public abstract class JTextComponent extends JComponent implements Scrollable, Accessible
 295 {
 296     /**
 297      * Creates a new <code>JTextComponent</code>.
 298      * Listeners for caret events are established, and the pluggable
 299      * UI installed.  The component is marked as editable.  No layout manager
 300      * is used, because layout is managed by the view subsystem of text.
 301      * The document model is set to <code>null</code>.
 302      */
 303     public JTextComponent() {
 304         super();
 305         // enable InputMethodEvent for on-the-spot pre-editing
 306         enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.INPUT_METHOD_EVENT_MASK);
 307         caretEvent = new MutableCaretEvent(this);
 308         addMouseListener(caretEvent);
 309         addFocusListener(caretEvent);
 310         setEditable(true);
 311         setDragEnabled(false);
 312         setLayout(null); // layout is managed by View hierarchy
 313         updateUI();
 314     }
 315 
 316     /**
 317      * Fetches the user-interface factory for this text-oriented editor.
 318      *
 319      * @return the factory
 320      */
 321     public TextUI getUI() { return (TextUI)ui; }
 322 
 323     /**
 324      * Sets the user-interface factory for this text-oriented editor.
 325      *
 326      * @param ui the factory
 327      */
 328     public void setUI(TextUI ui) {
 329         super.setUI(ui);
 330     }
 331 
 332     /**
 333      * Reloads the pluggable UI.  The key used to fetch the
 334      * new interface is <code>getUIClassID()</code>.  The type of
 335      * the UI is <code>TextUI</code>.  <code>invalidate</code>
 336      * is called after setting the UI.
 337      */
 338     public void updateUI() {
 339         setUI((TextUI)UIManager.getUI(this));
 340         invalidate();
 341     }
 342 
 343     /**
 344      * Adds a caret listener for notification of any changes
 345      * to the caret.
 346      *
 347      * @param listener the listener to be added
 348      * @see javax.swing.event.CaretEvent
 349      */
 350     public void addCaretListener(CaretListener listener) {
 351         listenerList.add(CaretListener.class, listener);
 352     }
 353 
 354     /**
 355      * Removes a caret listener.
 356      *
 357      * @param listener the listener to be removed
 358      * @see javax.swing.event.CaretEvent
 359      */
 360     public void removeCaretListener(CaretListener listener) {
 361         listenerList.remove(CaretListener.class, listener);
 362     }
 363 
 364     /**
 365      * Returns an array of all the caret listeners
 366      * registered on this text component.
 367      *
 368      * @return all of this component's <code>CaretListener</code>s
 369      *         or an empty
 370      *         array if no caret listeners are currently registered
 371      *
 372      * @see #addCaretListener
 373      * @see #removeCaretListener
 374      *
 375      * @since 1.4
 376      */
 377     @BeanProperty(bound = false)
 378     public CaretListener[] getCaretListeners() {
 379         return listenerList.getListeners(CaretListener.class);
 380     }
 381 
 382     /**
 383      * Notifies all listeners that have registered interest for
 384      * notification on this event type.  The event instance
 385      * is lazily created using the parameters passed into
 386      * the fire method.  The listener list is processed in a
 387      * last-to-first manner.
 388      *
 389      * @param e the event
 390      * @see EventListenerList
 391      */
 392     protected void fireCaretUpdate(CaretEvent e) {
 393         // Guaranteed to return a non-null array
 394         Object[] listeners = listenerList.getListenerList();
 395         // Process the listeners last to first, notifying
 396         // those that are interested in this event
 397         for (int i = listeners.length-2; i>=0; i-=2) {
 398             if (listeners[i]==CaretListener.class) {
 399                 ((CaretListener)listeners[i+1]).caretUpdate(e);
 400             }
 401         }
 402     }
 403 
 404     /**
 405      * Associates the editor with a text document.
 406      * The currently registered factory is used to build a view for
 407      * the document, which gets displayed by the editor after revalidation.
 408      * A PropertyChange event ("document") is propagated to each listener.
 409      *
 410      * @param doc  the document to display/edit
 411      * @see #getDocument
 412      */
 413     @BeanProperty(expert = true, description
 414             = "the text document model")
 415     public void setDocument(Document doc) {
 416         Document old = model;
 417 
 418         /*
 419          * acquire a read lock on the old model to prevent notification of
 420          * mutations while we disconnecting the old model.
 421          */
 422         try {
 423             if (old instanceof AbstractDocument) {
 424                 ((AbstractDocument)old).readLock();
 425             }
 426             if (accessibleContext != null) {
 427                 model.removeDocumentListener(
 428                     ((AccessibleJTextComponent)accessibleContext));
 429             }
 430             if (inputMethodRequestsHandler != null) {
 431                 model.removeDocumentListener((DocumentListener)inputMethodRequestsHandler);
 432             }
 433             model = doc;
 434 
 435             // Set the document's run direction property to match the
 436             // component's ComponentOrientation property.
 437             Boolean runDir = getComponentOrientation().isLeftToRight()
 438                              ? TextAttribute.RUN_DIRECTION_LTR
 439                              : TextAttribute.RUN_DIRECTION_RTL;
 440             if (runDir != doc.getProperty(TextAttribute.RUN_DIRECTION)) {
 441                 doc.putProperty(TextAttribute.RUN_DIRECTION, runDir );
 442             }
 443             firePropertyChange("document", old, doc);
 444         } finally {
 445             if (old instanceof AbstractDocument) {
 446                 ((AbstractDocument)old).readUnlock();
 447             }
 448         }
 449 
 450         revalidate();
 451         repaint();
 452         if (accessibleContext != null) {
 453             model.addDocumentListener(
 454                 ((AccessibleJTextComponent)accessibleContext));
 455         }
 456         if (inputMethodRequestsHandler != null) {
 457             model.addDocumentListener((DocumentListener)inputMethodRequestsHandler);
 458         }
 459     }
 460 
 461     /**
 462      * Fetches the model associated with the editor.  This is
 463      * primarily for the UI to get at the minimal amount of
 464      * state required to be a text editor.  Subclasses will
 465      * return the actual type of the model which will typically
 466      * be something that extends Document.
 467      *
 468      * @return the model
 469      */
 470     public Document getDocument() {
 471         return model;
 472     }
 473 
 474     // Override of Component.setComponentOrientation
 475     public void setComponentOrientation( ComponentOrientation o ) {
 476         // Set the document's run direction property to match the
 477         // ComponentOrientation property.
 478         Document doc = getDocument();
 479         if( doc !=  null ) {
 480             Boolean runDir = o.isLeftToRight()
 481                              ? TextAttribute.RUN_DIRECTION_LTR
 482                              : TextAttribute.RUN_DIRECTION_RTL;
 483             doc.putProperty( TextAttribute.RUN_DIRECTION, runDir );
 484         }
 485         super.setComponentOrientation( o );
 486     }
 487 
 488     /**
 489      * Fetches the command list for the editor.  This is
 490      * the list of commands supported by the plugged-in UI
 491      * augmented by the collection of commands that the
 492      * editor itself supports.  These are useful for binding
 493      * to events, such as in a keymap.
 494      *
 495      * @return the command list
 496      */
 497     @BeanProperty(bound = false)
 498     public Action[] getActions() {
 499         return getUI().getEditorKit(this).getActions();
 500     }
 501 
 502     /**
 503      * Sets margin space between the text component's border
 504      * and its text.  The text component's default <code>Border</code>
 505      * object will use this value to create the proper margin.
 506      * However, if a non-default border is set on the text component,
 507      * it is that <code>Border</code> object's responsibility to create the
 508      * appropriate margin space (else this property will effectively
 509      * be ignored).  This causes a redraw of the component.
 510      * A PropertyChange event ("margin") is sent to all listeners.
 511      *
 512      * @param m the space between the border and the text
 513      */
 514     @BeanProperty(description
 515             = "desired space between the border and text area")
 516     public void setMargin(Insets m) {
 517         Insets old = margin;
 518         margin = m;
 519         firePropertyChange("margin", old, m);
 520         invalidate();
 521     }
 522 
 523     /**
 524      * Returns the margin between the text component's border and
 525      * its text.
 526      *
 527      * @return the margin
 528      */
 529     public Insets getMargin() {
 530         return margin;
 531     }
 532 
 533     /**
 534      * Sets the <code>NavigationFilter</code>. <code>NavigationFilter</code>
 535      * is used by <code>DefaultCaret</code> and the default cursor movement
 536      * actions as a way to restrict the cursor movement.
 537      *
 538      * @since 1.4
 539      */
 540     public void setNavigationFilter(NavigationFilter filter) {
 541         navigationFilter = filter;
 542     }
 543 
 544     /**
 545      * Returns the <code>NavigationFilter</code>. <code>NavigationFilter</code>
 546      * is used by <code>DefaultCaret</code> and the default cursor movement
 547      * actions as a way to restrict the cursor movement. A null return value
 548      * implies the cursor movement and selection should not be restricted.
 549      *
 550      * @since 1.4
 551      * @return the NavigationFilter
 552      */
 553     public NavigationFilter getNavigationFilter() {
 554         return navigationFilter;
 555     }
 556 
 557     /**
 558      * Fetches the caret that allows text-oriented navigation over
 559      * the view.
 560      *
 561      * @return the caret
 562      */
 563     @Transient
 564     public Caret getCaret() {
 565         return caret;
 566     }
 567 
 568     /**
 569      * Sets the caret to be used.  By default this will be set
 570      * by the UI that gets installed.  This can be changed to
 571      * a custom caret if desired.  Setting the caret results in a
 572      * PropertyChange event ("caret") being fired.
 573      *
 574      * @param c the caret
 575      * @see #getCaret
 576      */
 577     @BeanProperty(expert = true, description
 578             = "the caret used to select/navigate")
 579     public void setCaret(Caret c) {
 580         if (caret != null) {
 581             caret.removeChangeListener(caretEvent);
 582             caret.deinstall(this);
 583         }
 584         Caret old = caret;
 585         caret = c;
 586         if (caret != null) {
 587             caret.install(this);
 588             caret.addChangeListener(caretEvent);
 589         }
 590         firePropertyChange("caret", old, caret);
 591     }
 592 
 593     /**
 594      * Fetches the object responsible for making highlights.
 595      *
 596      * @return the highlighter
 597      */
 598     public Highlighter getHighlighter() {
 599         return highlighter;
 600     }
 601 
 602     /**
 603      * Sets the highlighter to be used.  By default this will be set
 604      * by the UI that gets installed.  This can be changed to
 605      * a custom highlighter if desired.  The highlighter can be set to
 606      * <code>null</code> to disable it.
 607      * A PropertyChange event ("highlighter") is fired
 608      * when a new highlighter is installed.
 609      *
 610      * @param h the highlighter
 611      * @see #getHighlighter
 612      */
 613     @BeanProperty(expert = true, description
 614             = "object responsible for background highlights")
 615     public void setHighlighter(Highlighter h) {
 616         if (highlighter != null) {
 617             highlighter.deinstall(this);
 618         }
 619         Highlighter old = highlighter;
 620         highlighter = h;
 621         if (highlighter != null) {
 622             highlighter.install(this);
 623         }
 624         firePropertyChange("highlighter", old, h);
 625     }
 626 
 627     /**
 628      * Sets the keymap to use for binding events to
 629      * actions.  Setting to <code>null</code> effectively disables
 630      * keyboard input.
 631      * A PropertyChange event ("keymap") is fired when a new keymap
 632      * is installed.
 633      *
 634      * @param map the keymap
 635      * @see #getKeymap
 636      */
 637     @BeanProperty(description
 638             = "set of key event to action bindings to use")
 639     public void setKeymap(Keymap map) {
 640         Keymap old = keymap;
 641         keymap = map;
 642         firePropertyChange("keymap", old, keymap);
 643         updateInputMap(old, map);
 644     }
 645 
 646     /**
 647      * Turns on or off automatic drag handling. In order to enable automatic
 648      * drag handling, this property should be set to {@code true}, and the
 649      * component's {@code TransferHandler} needs to be {@code non-null}.
 650      * The default value of the {@code dragEnabled} property is {@code false}.
 651      * <p>
 652      * The job of honoring this property, and recognizing a user drag gesture,
 653      * lies with the look and feel implementation, and in particular, the component's
 654      * {@code TextUI}. When automatic drag handling is enabled, most look and
 655      * feels (including those that subclass {@code BasicLookAndFeel}) begin a
 656      * drag and drop operation whenever the user presses the mouse button over
 657      * a selection and then moves the mouse a few pixels. Setting this property to
 658      * {@code true} can therefore have a subtle effect on how selections behave.
 659      * <p>
 660      * If a look and feel is used that ignores this property, you can still
 661      * begin a drag and drop operation by calling {@code exportAsDrag} on the
 662      * component's {@code TransferHandler}.
 663      *
 664      * @param b whether or not to enable automatic drag handling
 665      * @exception HeadlessException if
 666      *            <code>b</code> is <code>true</code> and
 667      *            <code>GraphicsEnvironment.isHeadless()</code>
 668      *            returns <code>true</code>
 669      * @see java.awt.GraphicsEnvironment#isHeadless
 670      * @see #getDragEnabled
 671      * @see #setTransferHandler
 672      * @see TransferHandler
 673      * @since 1.4
 674      */
 675     @BeanProperty(bound = false, description
 676             = "determines whether automatic drag handling is enabled")
 677     public void setDragEnabled(boolean b) {
 678         checkDragEnabled(b);
 679         dragEnabled = b;
 680     }
 681 
 682     private static void checkDragEnabled(boolean b) {
 683         if (b && GraphicsEnvironment.isHeadless()) {
 684             throw new HeadlessException();
 685         }
 686     }
 687 
 688     /**
 689      * Returns whether or not automatic drag handling is enabled.
 690      *
 691      * @return the value of the {@code dragEnabled} property
 692      * @see #setDragEnabled
 693      * @since 1.4
 694      */
 695     public boolean getDragEnabled() {
 696         return dragEnabled;
 697     }
 698 
 699     /**
 700      * Sets the drop mode for this component. For backward compatibility,
 701      * the default for this property is <code>DropMode.USE_SELECTION</code>.
 702      * Usage of <code>DropMode.INSERT</code> is recommended, however,
 703      * for an improved user experience. It offers similar behavior of dropping
 704      * between text locations, but does so without affecting the actual text
 705      * selection and caret location.
 706      * <p>
 707      * <code>JTextComponents</code> support the following drop modes:
 708      * <ul>
 709      *    <li><code>DropMode.USE_SELECTION</code></li>
 710      *    <li><code>DropMode.INSERT</code></li>
 711      * </ul>
 712      * <p>
 713      * The drop mode is only meaningful if this component has a
 714      * <code>TransferHandler</code> that accepts drops.
 715      *
 716      * @param dropMode the drop mode to use
 717      * @throws IllegalArgumentException if the drop mode is unsupported
 718      *         or <code>null</code>
 719      * @see #getDropMode
 720      * @see #getDropLocation
 721      * @see #setTransferHandler
 722      * @see javax.swing.TransferHandler
 723      * @since 1.6
 724      */
 725     public final void setDropMode(DropMode dropMode) {
 726         checkDropMode(dropMode);
 727         this.dropMode = dropMode;
 728     }
 729 
 730     private static void checkDropMode(DropMode dropMode) {
 731         if (dropMode != null) {
 732             switch (dropMode) {
 733                 case USE_SELECTION:
 734                 case INSERT:
 735                     return;
 736             }
 737         }
 738 
 739         throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for text");
 740     }
 741 
 742     /**
 743      * Returns the drop mode for this component.
 744      *
 745      * @return the drop mode for this component
 746      * @see #setDropMode
 747      * @since 1.6
 748      */
 749     public final DropMode getDropMode() {
 750         return dropMode;
 751     }
 752 
 753     static {
 754         SwingAccessor.setJTextComponentAccessor(
 755             new SwingAccessor.JTextComponentAccessor() {
 756                 public TransferHandler.DropLocation dropLocationForPoint(JTextComponent textComp,
 757                                                                          Point p)
 758                 {
 759                     return textComp.dropLocationForPoint(p);
 760                 }
 761                 public Object setDropLocation(JTextComponent textComp,
 762                                               TransferHandler.DropLocation location,
 763                                               Object state, boolean forDrop)
 764                 {
 765                     return textComp.setDropLocation(location, state, forDrop);
 766                 }
 767             });
 768     }
 769 
 770 
 771     /**
 772      * Calculates a drop location in this component, representing where a
 773      * drop at the given point should insert data.
 774      * <p>
 775      * Note: This method is meant to override
 776      * <code>JComponent.dropLocationForPoint()</code>, which is package-private
 777      * in javax.swing. <code>TransferHandler</code> will detect text components
 778      * and call this method instead via reflection. It's name should therefore
 779      * not be changed.
 780      *
 781      * @param p the point to calculate a drop location for
 782      * @return the drop location, or <code>null</code>
 783      */
 784     DropLocation dropLocationForPoint(Point p) {
 785         Position.Bias[] bias = new Position.Bias[1];
 786         int index = getUI().viewToModel(this, p, bias);
 787 
 788         // viewToModel currently returns null for some HTML content
 789         // when the point is within the component's top inset
 790         if (bias[0] == null) {
 791             bias[0] = Position.Bias.Forward;
 792         }
 793 
 794         return new DropLocation(p, index, bias[0]);
 795     }
 796 
 797     /**
 798      * Called to set or clear the drop location during a DnD operation.
 799      * In some cases, the component may need to use it's internal selection
 800      * temporarily to indicate the drop location. To help facilitate this,
 801      * this method returns and accepts as a parameter a state object.
 802      * This state object can be used to store, and later restore, the selection
 803      * state. Whatever this method returns will be passed back to it in
 804      * future calls, as the state parameter. If it wants the DnD system to
 805      * continue storing the same state, it must pass it back every time.
 806      * Here's how this is used:
 807      * <p>
 808      * Let's say that on the first call to this method the component decides
 809      * to save some state (because it is about to use the selection to show
 810      * a drop index). It can return a state object to the caller encapsulating
 811      * any saved selection state. On a second call, let's say the drop location
 812      * is being changed to something else. The component doesn't need to
 813      * restore anything yet, so it simply passes back the same state object
 814      * to have the DnD system continue storing it. Finally, let's say this
 815      * method is messaged with <code>null</code>. This means DnD
 816      * is finished with this component for now, meaning it should restore
 817      * state. At this point, it can use the state parameter to restore
 818      * said state, and of course return <code>null</code> since there's
 819      * no longer anything to store.
 820      * <p>
 821      * Note: This method is meant to override
 822      * <code>JComponent.setDropLocation()</code>, which is package-private
 823      * in javax.swing. <code>TransferHandler</code> will detect text components
 824      * and call this method instead via reflection. It's name should therefore
 825      * not be changed.
 826      *
 827      * @param location the drop location (as calculated by
 828      *        <code>dropLocationForPoint</code>) or <code>null</code>
 829      *        if there's no longer a valid drop location
 830      * @param state the state object saved earlier for this component,
 831      *        or <code>null</code>
 832      * @param forDrop whether or not the method is being called because an
 833      *        actual drop occurred
 834      * @return any saved state for this component, or <code>null</code> if none
 835      */
 836     Object setDropLocation(TransferHandler.DropLocation location,
 837                            Object state,
 838                            boolean forDrop) {
 839 
 840         Object retVal = null;
 841         DropLocation textLocation = (DropLocation)location;
 842 
 843         if (dropMode == DropMode.USE_SELECTION) {
 844             if (textLocation == null) {
 845                 if (state != null) {
 846                     /*
 847                      * This object represents the state saved earlier.
 848                      *     If the caret is a DefaultCaret it will be
 849                      *     an Object array containing, in order:
 850                      *         - the saved caret mark (Integer)
 851                      *         - the saved caret dot (Integer)
 852                      *         - the saved caret visibility (Boolean)
 853                      *         - the saved mark bias (Position.Bias)
 854                      *         - the saved dot bias (Position.Bias)
 855                      *     If the caret is not a DefaultCaret it will
 856                      *     be similar, but will not contain the dot
 857                      *     or mark bias.
 858                      */
 859                     Object[] vals = (Object[])state;
 860 
 861                     if (!forDrop) {
 862                         if (caret instanceof DefaultCaret) {
 863                             ((DefaultCaret)caret).setDot(((Integer)vals[0]).intValue(),
 864                                                          (Position.Bias)vals[3]);
 865                             ((DefaultCaret)caret).moveDot(((Integer)vals[1]).intValue(),
 866                                                          (Position.Bias)vals[4]);
 867                         } else {
 868                             caret.setDot(((Integer)vals[0]).intValue());
 869                             caret.moveDot(((Integer)vals[1]).intValue());
 870                         }
 871                     }
 872 
 873                     caret.setVisible(((Boolean)vals[2]).booleanValue());
 874                 }
 875             } else {
 876                 if (dropLocation == null) {
 877                     boolean visible;
 878 
 879                     if (caret instanceof DefaultCaret) {
 880                         DefaultCaret dc = (DefaultCaret)caret;
 881                         visible = dc.isActive();
 882                         retVal = new Object[] {Integer.valueOf(dc.getMark()),
 883                                                Integer.valueOf(dc.getDot()),
 884                                                Boolean.valueOf(visible),
 885                                                dc.getMarkBias(),
 886                                                dc.getDotBias()};
 887                     } else {
 888                         visible = caret.isVisible();
 889                         retVal = new Object[] {Integer.valueOf(caret.getMark()),
 890                                                Integer.valueOf(caret.getDot()),
 891                                                Boolean.valueOf(visible)};
 892                     }
 893 
 894                     caret.setVisible(true);
 895                 } else {
 896                     retVal = state;
 897                 }
 898 
 899                 if (caret instanceof DefaultCaret) {
 900                     ((DefaultCaret)caret).setDot(textLocation.getIndex(), textLocation.getBias());
 901                 } else {
 902                     caret.setDot(textLocation.getIndex());
 903                 }
 904             }
 905         } else {
 906             if (textLocation == null) {
 907                 if (state != null) {
 908                     caret.setVisible(((Boolean)state).booleanValue());
 909                 }
 910             } else {
 911                 if (dropLocation == null) {
 912                     boolean visible = caret instanceof DefaultCaret
 913                                       ? ((DefaultCaret)caret).isActive()
 914                                       : caret.isVisible();
 915                     retVal = Boolean.valueOf(visible);
 916                     caret.setVisible(false);
 917                 } else {
 918                     retVal = state;
 919                 }
 920             }
 921         }
 922 
 923         DropLocation old = dropLocation;
 924         dropLocation = textLocation;
 925         firePropertyChange("dropLocation", old, dropLocation);
 926 
 927         return retVal;
 928     }
 929 
 930     /**
 931      * Returns the location that this component should visually indicate
 932      * as the drop location during a DnD operation over the component,
 933      * or {@code null} if no location is to currently be shown.
 934      * <p>
 935      * This method is not meant for querying the drop location
 936      * from a {@code TransferHandler}, as the drop location is only
 937      * set after the {@code TransferHandler}'s <code>canImport</code>
 938      * has returned and has allowed for the location to be shown.
 939      * <p>
 940      * When this property changes, a property change event with
 941      * name "dropLocation" is fired by the component.
 942      *
 943      * @return the drop location
 944      * @see #setDropMode
 945      * @see TransferHandler#canImport(TransferHandler.TransferSupport)
 946      * @since 1.6
 947      */
 948     @BeanProperty(bound = false)
 949     public final DropLocation getDropLocation() {
 950         return dropLocation;
 951     }
 952 
 953 
 954     /**
 955      * Updates the <code>InputMap</code>s in response to a
 956      * <code>Keymap</code> change.
 957      * @param oldKm  the old <code>Keymap</code>
 958      * @param newKm  the new <code>Keymap</code>
 959      */
 960     void updateInputMap(Keymap oldKm, Keymap newKm) {
 961         // Locate the current KeymapWrapper.
 962         InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
 963         InputMap last = km;
 964         while (km != null && !(km instanceof KeymapWrapper)) {
 965             last = km;
 966             km = km.getParent();
 967         }
 968         if (km != null) {
 969             // Found it, tweak the InputMap that points to it, as well
 970             // as anything it points to.
 971             if (newKm == null) {
 972                 if (last != km) {
 973                     last.setParent(km.getParent());
 974                 }
 975                 else {
 976                     last.setParent(null);
 977                 }
 978             }
 979             else {
 980                 InputMap newKM = new KeymapWrapper(newKm);
 981                 last.setParent(newKM);
 982                 if (last != km) {
 983                     newKM.setParent(km.getParent());
 984                 }
 985             }
 986         }
 987         else if (newKm != null) {
 988             km = getInputMap(JComponent.WHEN_FOCUSED);
 989             if (km != null) {
 990                 // Couldn't find it.
 991                 // Set the parent of WHEN_FOCUSED InputMap to be the new one.
 992                 InputMap newKM = new KeymapWrapper(newKm);
 993                 newKM.setParent(km.getParent());
 994                 km.setParent(newKM);
 995             }
 996         }
 997 
 998         // Do the same thing with the ActionMap
 999         ActionMap am = getActionMap();
1000         ActionMap lastAM = am;
1001         while (am != null && !(am instanceof KeymapActionMap)) {
1002             lastAM = am;
1003             am = am.getParent();
1004         }
1005         if (am != null) {
1006             // Found it, tweak the Actionap that points to it, as well
1007             // as anything it points to.
1008             if (newKm == null) {
1009                 if (lastAM != am) {
1010                     lastAM.setParent(am.getParent());
1011                 }
1012                 else {
1013                     lastAM.setParent(null);
1014                 }
1015             }
1016             else {
1017                 ActionMap newAM = new KeymapActionMap(newKm);
1018                 lastAM.setParent(newAM);
1019                 if (lastAM != am) {
1020                     newAM.setParent(am.getParent());
1021                 }
1022             }
1023         }
1024         else if (newKm != null) {
1025             am = getActionMap();
1026             if (am != null) {
1027                 // Couldn't find it.
1028                 // Set the parent of ActionMap to be the new one.
1029                 ActionMap newAM = new KeymapActionMap(newKm);
1030                 newAM.setParent(am.getParent());
1031                 am.setParent(newAM);
1032             }
1033         }
1034     }
1035 
1036     /**
1037      * Fetches the keymap currently active in this text
1038      * component.
1039      *
1040      * @return the keymap
1041      */
1042     public Keymap getKeymap() {
1043         return keymap;
1044     }
1045 
1046     /**
1047      * Adds a new keymap into the keymap hierarchy.  Keymap bindings
1048      * resolve from bottom up so an attribute specified in a child
1049      * will override an attribute specified in the parent.
1050      *
1051      * @param nm   the name of the keymap (must be unique within the
1052      *   collection of named keymaps in the document); the name may
1053      *   be <code>null</code> if the keymap is unnamed,
1054      *   but the caller is responsible for managing the reference
1055      *   returned as an unnamed keymap can't
1056      *   be fetched by name
1057      * @param parent the parent keymap; this may be <code>null</code> if
1058      *   unspecified bindings need not be resolved in some other keymap
1059      * @return the keymap
1060      */
1061     public static Keymap addKeymap(String nm, Keymap parent) {
1062         Keymap map = new DefaultKeymap(nm, parent);
1063         if (nm != null) {
1064             // add a named keymap, a class of bindings
1065             getKeymapTable().put(nm, map);
1066         }
1067         return map;
1068     }
1069 
1070     /**
1071      * Removes a named keymap previously added to the document.  Keymaps
1072      * with <code>null</code> names may not be removed in this way.
1073      *
1074      * @param nm  the name of the keymap to remove
1075      * @return the keymap that was removed
1076      */
1077     public static Keymap removeKeymap(String nm) {
1078         return getKeymapTable().remove(nm);
1079     }
1080 
1081     /**
1082      * Fetches a named keymap previously added to the document.
1083      * This does not work with <code>null</code>-named keymaps.
1084      *
1085      * @param nm  the name of the keymap
1086      * @return the keymap
1087      */
1088     public static Keymap getKeymap(String nm) {
1089         return getKeymapTable().get(nm);
1090     }
1091 
1092     private static HashMap<String,Keymap> getKeymapTable() {
1093         synchronized (KEYMAP_TABLE) {
1094             AppContext appContext = AppContext.getAppContext();
1095             @SuppressWarnings("unchecked")
1096             HashMap<String,Keymap> keymapTable =
1097                 (HashMap<String,Keymap>)appContext.get(KEYMAP_TABLE);
1098             if (keymapTable == null) {
1099                 keymapTable = new HashMap<String,Keymap>(17);
1100                 appContext.put(KEYMAP_TABLE, keymapTable);
1101                 //initialize default keymap
1102                 Keymap binding = addKeymap(DEFAULT_KEYMAP, null);
1103                 binding.setDefaultAction(new
1104                                          DefaultEditorKit.DefaultKeyTypedAction());
1105             }
1106             return keymapTable;
1107         }
1108     }
1109 
1110     /**
1111      * Binding record for creating key bindings.
1112      * <p>
1113      * <strong>Warning:</strong>
1114      * Serialized objects of this class will not be compatible with
1115      * future Swing releases. The current serialization support is
1116      * appropriate for short term storage or RMI between applications running
1117      * the same version of Swing.  As of 1.4, support for long term storage
1118      * of all JavaBeans&trade;
1119      * has been added to the <code>java.beans</code> package.
1120      * Please see {@link java.beans.XMLEncoder}.
1121      */
1122     @SuppressWarnings("serial") // Same-version serialization only
1123     public static class KeyBinding {
1124 
1125         /**
1126          * The key.
1127          */
1128         public KeyStroke key;
1129 
1130         /**
1131          * The name of the action for the key.
1132          */
1133         public String actionName;
1134 
1135         /**
1136          * Creates a new key binding.
1137          *
1138          * @param key the key
1139          * @param actionName the name of the action for the key
1140          */
1141         public KeyBinding(KeyStroke key, String actionName) {
1142             this.key = key;
1143             this.actionName = actionName;
1144         }
1145     }
1146 
1147     /**
1148      * <p>
1149      * Loads a keymap with a bunch of
1150      * bindings.  This can be used to take a static table of
1151      * definitions and load them into some keymap.  The following
1152      * example illustrates an example of binding some keys to
1153      * the cut, copy, and paste actions associated with a
1154      * JTextComponent.  A code fragment to accomplish
1155      * this might look as follows:
1156      * <pre><code>
1157      *
1158      *   static final JTextComponent.KeyBinding[] defaultBindings = {
1159      *     new JTextComponent.KeyBinding(
1160      *       KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
1161      *       DefaultEditorKit.copyAction),
1162      *     new JTextComponent.KeyBinding(
1163      *       KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
1164      *       DefaultEditorKit.pasteAction),
1165      *     new JTextComponent.KeyBinding(
1166      *       KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
1167      *       DefaultEditorKit.cutAction),
1168      *   };
1169      *
1170      *   JTextComponent c = new JTextPane();
1171      *   Keymap k = c.getKeymap();
1172      *   JTextComponent.loadKeymap(k, defaultBindings, c.getActions());
1173      *
1174      * </code></pre>
1175      * The sets of bindings and actions may be empty but must be
1176      * non-<code>null</code>.
1177      *
1178      * @param map the keymap
1179      * @param bindings the bindings
1180      * @param actions the set of actions
1181      */
1182     public static void loadKeymap(Keymap map, KeyBinding[] bindings, Action[] actions) {
1183         Hashtable<String, Action> h = new Hashtable<String, Action>();
1184         for (Action a : actions) {
1185             String value = (String)a.getValue(Action.NAME);
1186             h.put((value!=null ? value:""), a);
1187         }
1188         for (KeyBinding binding : bindings) {
1189             Action a = h.get(binding.actionName);
1190             if (a != null) {
1191                 map.addActionForKeyStroke(binding.key, a);
1192             }
1193         }
1194     }
1195 
1196     /**
1197      * Fetches the current color used to render the
1198      * caret.
1199      *
1200      * @return the color
1201      */
1202     public Color getCaretColor() {
1203         return caretColor;
1204     }
1205 
1206     /**
1207      * Sets the current color used to render the caret.
1208      * Setting to <code>null</code> effectively restores the default color.
1209      * Setting the color results in a PropertyChange event ("caretColor")
1210      * being fired.
1211      *
1212      * @param c the color
1213      * @see #getCaretColor
1214      */
1215     @BeanProperty(preferred = true, description
1216             = "the color used to render the caret")
1217     public void setCaretColor(Color c) {
1218         Color old = caretColor;
1219         caretColor = c;
1220         firePropertyChange("caretColor", old, caretColor);
1221     }
1222 
1223     /**
1224      * Fetches the current color used to render the
1225      * selection.
1226      *
1227      * @return the color
1228      */
1229     public Color getSelectionColor() {
1230         return selectionColor;
1231     }
1232 
1233     /**
1234      * Sets the current color used to render the selection.
1235      * Setting the color to <code>null</code> is the same as setting
1236      * <code>Color.white</code>.  Setting the color results in a
1237      * PropertyChange event ("selectionColor").
1238      *
1239      * @param c the color
1240      * @see #getSelectionColor
1241      */
1242     @BeanProperty(preferred = true, description
1243             = "color used to render selection background")
1244     public void setSelectionColor(Color c) {
1245         Color old = selectionColor;
1246         selectionColor = c;
1247         firePropertyChange("selectionColor", old, selectionColor);
1248     }
1249 
1250     /**
1251      * Fetches the current color used to render the
1252      * selected text.
1253      *
1254      * @return the color
1255      */
1256     public Color getSelectedTextColor() {
1257         return selectedTextColor;
1258     }
1259 
1260     /**
1261      * Sets the current color used to render the selected text.
1262      * Setting the color to <code>null</code> is the same as
1263      * <code>Color.black</code>. Setting the color results in a
1264      * PropertyChange event ("selectedTextColor") being fired.
1265      *
1266      * @param c the color
1267      * @see #getSelectedTextColor
1268      */
1269     @BeanProperty(preferred = true, description
1270             = "color used to render selected text")
1271     public void setSelectedTextColor(Color c) {
1272         Color old = selectedTextColor;
1273         selectedTextColor = c;
1274         firePropertyChange("selectedTextColor", old, selectedTextColor);
1275     }
1276 
1277     /**
1278      * Fetches the current color used to render the
1279      * disabled text.
1280      *
1281      * @return the color
1282      */
1283     public Color getDisabledTextColor() {
1284         return disabledTextColor;
1285     }
1286 
1287     /**
1288      * Sets the current color used to render the
1289      * disabled text.  Setting the color fires off a
1290      * PropertyChange event ("disabledTextColor").
1291      *
1292      * @param c the color
1293      * @see #getDisabledTextColor
1294      */
1295     @BeanProperty(preferred = true, description
1296             = "color used to render disabled text")
1297     public void setDisabledTextColor(Color c) {
1298         Color old = disabledTextColor;
1299         disabledTextColor = c;
1300         firePropertyChange("disabledTextColor", old, disabledTextColor);
1301     }
1302 
1303     /**
1304      * Replaces the currently selected content with new content
1305      * represented by the given string.  If there is no selection
1306      * this amounts to an insert of the given text.  If there
1307      * is no replacement text this amounts to a removal of the
1308      * current selection.
1309      * <p>
1310      * This is the method that is used by the default implementation
1311      * of the action for inserting content that gets bound to the
1312      * keymap actions.
1313      *
1314      * @param content  the content to replace the selection with
1315      */
1316     public void replaceSelection(String content) {
1317         Document doc = getDocument();
1318         if (doc != null) {
1319             try {
1320                 boolean composedTextSaved = saveComposedText(caret.getDot());
1321                 int p0 = Math.min(caret.getDot(), caret.getMark());
1322                 int p1 = Math.max(caret.getDot(), caret.getMark());
1323                 if (doc instanceof AbstractDocument) {
1324                     ((AbstractDocument)doc).replace(p0, p1 - p0, content,null);
1325                 }
1326                 else {
1327                     if (p0 != p1) {
1328                         doc.remove(p0, p1 - p0);
1329                     }
1330                     if (content != null && content.length() > 0) {
1331                         doc.insertString(p0, content, null);
1332                     }
1333                 }
1334                 if (composedTextSaved) {
1335                     restoreComposedText();
1336                 }
1337             } catch (BadLocationException e) {
1338                 UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
1339             }
1340         }
1341     }
1342 
1343     /**
1344      * Fetches a portion of the text represented by the
1345      * component.  Returns an empty string if length is 0.
1346      *
1347      * @param offs the offset &ge; 0
1348      * @param len the length &ge; 0
1349      * @return the text
1350      * @exception BadLocationException if the offset or length are invalid
1351      */
1352     public String getText(int offs, int len) throws BadLocationException {
1353         return getDocument().getText(offs, len);
1354     }
1355 
1356     /**
1357      * Converts the given location in the model to a place in
1358      * the view coordinate system.
1359      * The component must have a positive size for
1360      * this translation to be computed (i.e. layout cannot
1361      * be computed until the component has been sized).  The
1362      * component does not have to be visible or painted.
1363      *
1364      * @param pos the position &ge; 0
1365      * @return the coordinates as a rectangle, with (r.x, r.y) as the location
1366      *   in the coordinate system, or null if the component does
1367      *   not yet have a positive size.
1368      * @exception BadLocationException if the given position does not
1369      *   represent a valid location in the associated document
1370      * @see TextUI#modelToView
1371      */
1372     public Rectangle modelToView(int pos) throws BadLocationException {
1373         return getUI().modelToView(this, pos);
1374     }
1375 
1376     /**
1377      * Converts the given place in the view coordinate system
1378      * to the nearest representative location in the model.
1379      * The component must have a positive size for
1380      * this translation to be computed (i.e. layout cannot
1381      * be computed until the component has been sized).  The
1382      * component does not have to be visible or painted.
1383      *
1384      * @param pt the location in the view to translate
1385      * @return the offset &ge; 0 from the start of the document,
1386      *   or -1 if the component does not yet have a positive
1387      *   size.
1388      * @see TextUI#viewToModel
1389      */
1390     public int viewToModel(Point pt) {
1391         return getUI().viewToModel(this, pt);
1392     }
1393 
1394     /**
1395      * Transfers the currently selected range in the associated
1396      * text model to the system clipboard, removing the contents
1397      * from the model.  The current selection is reset.  Does nothing
1398      * for <code>null</code> selections.
1399      *
1400      * @see java.awt.Toolkit#getSystemClipboard
1401      * @see java.awt.datatransfer.Clipboard
1402      */
1403     public void cut() {
1404         if (isEditable() && isEnabled()) {
1405             invokeAction("cut", TransferHandler.getCutAction());
1406         }
1407     }
1408 
1409     /**
1410      * Transfers the currently selected range in the associated
1411      * text model to the system clipboard, leaving the contents
1412      * in the text model.  The current selection remains intact.
1413      * Does nothing for <code>null</code> selections.
1414      *
1415      * @see java.awt.Toolkit#getSystemClipboard
1416      * @see java.awt.datatransfer.Clipboard
1417      */
1418     public void copy() {
1419         invokeAction("copy", TransferHandler.getCopyAction());
1420     }
1421 
1422     /**
1423      * Transfers the contents of the system clipboard into the
1424      * associated text model.  If there is a selection in the
1425      * associated view, it is replaced with the contents of the
1426      * clipboard.  If there is no selection, the clipboard contents
1427      * are inserted in front of the current insert position in
1428      * the associated view.  If the clipboard is empty, does nothing.
1429      *
1430      * @see #replaceSelection
1431      * @see java.awt.Toolkit#getSystemClipboard
1432      * @see java.awt.datatransfer.Clipboard
1433      */
1434     public void paste() {
1435         if (isEditable() && isEnabled()) {
1436             invokeAction("paste", TransferHandler.getPasteAction());
1437         }
1438     }
1439 
1440     /**
1441      * This is a convenience method that is only useful for
1442      * <code>cut</code>, <code>copy</code> and <code>paste</code>.  If
1443      * an <code>Action</code> with the name <code>name</code> does not
1444      * exist in the <code>ActionMap</code>, this will attempt to install a
1445      * <code>TransferHandler</code> and then use <code>altAction</code>.
1446      */
1447     private void invokeAction(String name, Action altAction) {
1448         ActionMap map = getActionMap();
1449         Action action = null;
1450 
1451         if (map != null) {
1452             action = map.get(name);
1453         }
1454         if (action == null) {
1455             installDefaultTransferHandlerIfNecessary();
1456             action = altAction;
1457         }
1458         action.actionPerformed(new ActionEvent(this,
1459                                ActionEvent.ACTION_PERFORMED, (String)action.
1460                                getValue(Action.NAME),
1461                                EventQueue.getMostRecentEventTime(),
1462                                getCurrentEventModifiers()));
1463     }
1464 
1465     /**
1466      * If the current <code>TransferHandler</code> is null, this will
1467      * install a new one.
1468      */
1469     private void installDefaultTransferHandlerIfNecessary() {
1470         if (getTransferHandler() == null) {
1471             if (defaultTransferHandler == null) {
1472                 defaultTransferHandler = new DefaultTransferHandler();
1473             }
1474             setTransferHandler(defaultTransferHandler);
1475         }
1476     }
1477 
1478     /**
1479      * Moves the caret to a new position, leaving behind a mark
1480      * defined by the last time <code>setCaretPosition</code> was
1481      * called.  This forms a selection.
1482      * If the document is <code>null</code>, does nothing. The position
1483      * must be between 0 and the length of the component's text or else
1484      * an exception is thrown.
1485      *
1486      * @param pos the position
1487      * @exception    IllegalArgumentException if the value supplied
1488      *               for <code>position</code> is less than zero or greater
1489      *               than the component's text length
1490      * @see #setCaretPosition
1491      */
1492     public void moveCaretPosition(int pos) {
1493         Document doc = getDocument();
1494         if (doc != null) {
1495             if (pos > doc.getLength() || pos < 0) {
1496                 throw new IllegalArgumentException("bad position: " + pos);
1497             }
1498             caret.moveDot(pos);
1499         }
1500     }
1501 
1502     /**
1503      * The bound property name for the focus accelerator.
1504      */
1505     public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
1506 
1507     /**
1508      * Sets the key accelerator that will cause the receiving text
1509      * component to get the focus.  The accelerator will be the
1510      * key combination of the platform-specific modifier key and
1511      * the character given (converted to upper case).  For example,
1512      * the ALT key is used as a modifier on Windows and the CTRL+ALT
1513      * combination is used on Mac.  By default, there is no focus
1514      * accelerator key.  Any previous key accelerator setting will be
1515      * superseded.  A '\0' key setting will be registered, and has the
1516      * effect of turning off the focus accelerator.  When the new key
1517      * is set, a PropertyChange event (FOCUS_ACCELERATOR_KEY) will be fired.
1518      *
1519      * @param aKey the key
1520      * @see #getFocusAccelerator
1521      */
1522     @BeanProperty(description
1523             = "accelerator character used to grab focus")
1524     public void setFocusAccelerator(char aKey) {
1525         aKey = Character.toUpperCase(aKey);
1526         char old = focusAccelerator;
1527         focusAccelerator = aKey;
1528         // Fix for 4341002: value of FOCUS_ACCELERATOR_KEY is wrong.
1529         // So we fire both FOCUS_ACCELERATOR_KEY, for compatibility,
1530         // and the correct event here.
1531         firePropertyChange(FOCUS_ACCELERATOR_KEY, old, focusAccelerator);
1532         firePropertyChange("focusAccelerator", old, focusAccelerator);
1533     }
1534 
1535     /**
1536      * Returns the key accelerator that will cause the receiving
1537      * text component to get the focus.  Return '\0' if no focus
1538      * accelerator has been set.
1539      *
1540      * @return the key
1541      */
1542     public char getFocusAccelerator() {
1543         return focusAccelerator;
1544     }
1545 
1546     /**
1547      * Initializes from a stream.  This creates a
1548      * model of the type appropriate for the component
1549      * and initializes the model from the stream.
1550      * By default this will load the model as plain
1551      * text.  Previous contents of the model are discarded.
1552      *
1553      * @param in the stream to read from
1554      * @param desc an object describing the stream; this
1555      *   might be a string, a File, a URL, etc.  Some kinds
1556      *   of documents (such as html for example) might be
1557      *   able to make use of this information; if non-<code>null</code>,
1558      *   it is added as a property of the document
1559      * @exception IOException as thrown by the stream being
1560      *  used to initialize
1561      * @see EditorKit#createDefaultDocument
1562      * @see #setDocument
1563      * @see PlainDocument
1564      */
1565     public void read(Reader in, Object desc) throws IOException {
1566         EditorKit kit = getUI().getEditorKit(this);
1567         Document doc = kit.createDefaultDocument();
1568         if (desc != null) {
1569             doc.putProperty(Document.StreamDescriptionProperty, desc);
1570         }
1571         try {
1572             kit.read(in, doc, 0);
1573             setDocument(doc);
1574         } catch (BadLocationException e) {
1575             throw new IOException(e.getMessage());
1576         }
1577     }
1578 
1579     /**
1580      * Stores the contents of the model into the given
1581      * stream.  By default this will store the model as plain
1582      * text.
1583      *
1584      * @param out the output stream
1585      * @exception IOException on any I/O error
1586      */
1587     public void write(Writer out) throws IOException {
1588         Document doc = getDocument();
1589         try {
1590             getUI().getEditorKit(this).write(out, doc, 0, doc.getLength());
1591         } catch (BadLocationException e) {
1592             throw new IOException(e.getMessage());
1593         }
1594     }
1595 
1596     public void removeNotify() {
1597         super.removeNotify();
1598         if (getFocusedComponent() == this) {
1599             AppContext.getAppContext().remove(FOCUSED_COMPONENT);
1600         }
1601     }
1602 
1603     // --- java.awt.TextComponent methods ------------------------
1604 
1605     /**
1606      * Sets the position of the text insertion caret for the
1607      * <code>TextComponent</code>.  Note that the caret tracks change,
1608      * so this may move if the underlying text of the component is changed.
1609      * If the document is <code>null</code>, does nothing. The position
1610      * must be between 0 and the length of the component's text or else
1611      * an exception is thrown.
1612      *
1613      * @param position the position
1614      * @exception    IllegalArgumentException if the value supplied
1615      *               for <code>position</code> is less than zero or greater
1616      *               than the component's text length
1617      */
1618     @BeanProperty(bound = false, description
1619             = "the caret position")
1620     public void setCaretPosition(int position) {
1621         Document doc = getDocument();
1622         if (doc != null) {
1623             if (position > doc.getLength() || position < 0) {
1624                 throw new IllegalArgumentException("bad position: " + position);
1625             }
1626             caret.setDot(position);
1627         }
1628     }
1629 
1630     /**
1631      * Returns the position of the text insertion caret for the
1632      * text component.
1633      *
1634      * @return the position of the text insertion caret for the
1635      *  text component &ge; 0
1636      */
1637     @Transient
1638     public int getCaretPosition() {
1639         return caret.getDot();
1640     }
1641 
1642     /**
1643      * Sets the text of this <code>TextComponent</code>
1644      * to the specified text.  If the text is <code>null</code>
1645      * or empty, has the effect of simply deleting the old text.
1646      * When text has been inserted, the resulting caret location
1647      * is determined by the implementation of the caret class.
1648      *
1649      * <p>
1650      * Note that text is not a bound property, so no <code>PropertyChangeEvent
1651      * </code> is fired when it changes. To listen for changes to the text,
1652      * use <code>DocumentListener</code>.
1653      *
1654      * @param t the new text to be set
1655      * @see #getText
1656      * @see DefaultCaret
1657      */
1658     @BeanProperty(bound = false, description
1659             = "the text of this component")
1660     public void setText(String t) {
1661         try {
1662             Document doc = getDocument();
1663             if (doc instanceof AbstractDocument) {
1664                 ((AbstractDocument)doc).replace(0, doc.getLength(), t,null);
1665             }
1666             else {
1667                 doc.remove(0, doc.getLength());
1668                 doc.insertString(0, t, null);
1669             }
1670         } catch (BadLocationException e) {
1671             UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
1672         }
1673     }
1674 
1675     /**
1676      * Returns the text contained in this <code>TextComponent</code>.
1677      * If the underlying document is <code>null</code>,
1678      * will give a <code>NullPointerException</code>.
1679      *
1680      * Note that text is not a bound property, so no <code>PropertyChangeEvent
1681      * </code> is fired when it changes. To listen for changes to the text,
1682      * use <code>DocumentListener</code>.
1683      *
1684      * @return the text
1685      * @exception NullPointerException if the document is <code>null</code>
1686      * @see #setText
1687      */
1688     public String getText() {
1689         Document doc = getDocument();
1690         String txt;
1691         try {
1692             txt = doc.getText(0, doc.getLength());
1693         } catch (BadLocationException e) {
1694             txt = null;
1695         }
1696         return txt;
1697     }
1698 
1699     /**
1700      * Returns the selected text contained in this
1701      * <code>TextComponent</code>.  If the selection is
1702      * <code>null</code> or the document empty, returns <code>null</code>.
1703      *
1704      * @return the text
1705      * @exception IllegalArgumentException if the selection doesn't
1706      *  have a valid mapping into the document for some reason
1707      * @see #setText
1708      */
1709     @BeanProperty(bound = false)
1710     public String getSelectedText() {
1711         String txt = null;
1712         int p0 = Math.min(caret.getDot(), caret.getMark());
1713         int p1 = Math.max(caret.getDot(), caret.getMark());
1714         if (p0 != p1) {
1715             try {
1716                 Document doc = getDocument();
1717                 txt = doc.getText(p0, p1 - p0);
1718             } catch (BadLocationException e) {
1719                 throw new IllegalArgumentException(e.getMessage());
1720             }
1721         }
1722         return txt;
1723     }
1724 
1725     /**
1726      * Returns the boolean indicating whether this
1727      * <code>TextComponent</code> is editable or not.
1728      *
1729      * @return the boolean value
1730      * @see #setEditable
1731      */
1732     public boolean isEditable() {
1733         return editable;
1734     }
1735 
1736     /**
1737      * Sets the specified boolean to indicate whether or not this
1738      * <code>TextComponent</code> should be editable.
1739      * A PropertyChange event ("editable") is fired when the
1740      * state is changed.
1741      *
1742      * @param b the boolean to be set
1743      * @see #isEditable
1744      */
1745     @BeanProperty(description
1746             = "specifies if the text can be edited")
1747     public void setEditable(boolean b) {
1748         if (b != editable) {
1749             boolean oldVal = editable;
1750             editable = b;
1751             enableInputMethods(editable);
1752             firePropertyChange("editable", Boolean.valueOf(oldVal), Boolean.valueOf(editable));
1753             repaint();
1754         }
1755     }
1756 
1757     /**
1758      * Returns the selected text's start position.  Return 0 for an
1759      * empty document, or the value of dot if no selection.
1760      *
1761      * @return the start position &ge; 0
1762      */
1763     @Transient
1764     public int getSelectionStart() {
1765         int start = Math.min(caret.getDot(), caret.getMark());
1766         return start;
1767     }
1768 
1769     /**
1770      * Sets the selection start to the specified position.  The new
1771      * starting point is constrained to be before or at the current
1772      * selection end.
1773      * <p>
1774      * This is available for backward compatibility to code
1775      * that called this method on <code>java.awt.TextComponent</code>.
1776      * This is implemented to forward to the <code>Caret</code>
1777      * implementation which is where the actual selection is maintained.
1778      *
1779      * @param selectionStart the start position of the text &ge; 0
1780      */
1781     @BeanProperty(bound = false, description
1782             = "starting location of the selection.")
1783     public void setSelectionStart(int selectionStart) {
1784         /* Route through select method to enforce consistent policy
1785          * between selectionStart and selectionEnd.
1786          */
1787         select(selectionStart, getSelectionEnd());
1788     }
1789 
1790     /**
1791      * Returns the selected text's end position.  Return 0 if the document
1792      * is empty, or the value of dot if there is no selection.
1793      *
1794      * @return the end position &ge; 0
1795      */
1796     @Transient
1797     public int getSelectionEnd() {
1798         int end = Math.max(caret.getDot(), caret.getMark());
1799         return end;
1800     }
1801 
1802     /**
1803      * Sets the selection end to the specified position.  The new
1804      * end point is constrained to be at or after the current
1805      * selection start.
1806      * <p>
1807      * This is available for backward compatibility to code
1808      * that called this method on <code>java.awt.TextComponent</code>.
1809      * This is implemented to forward to the <code>Caret</code>
1810      * implementation which is where the actual selection is maintained.
1811      *
1812      * @param selectionEnd the end position of the text &ge; 0
1813      */
1814     @BeanProperty(bound = false, description
1815             = "ending location of the selection.")
1816     public void setSelectionEnd(int selectionEnd) {
1817         /* Route through select method to enforce consistent policy
1818          * between selectionStart and selectionEnd.
1819          */
1820         select(getSelectionStart(), selectionEnd);
1821     }
1822 
1823     /**
1824      * Selects the text between the specified start and end positions.
1825      * <p>
1826      * This method sets the start and end positions of the
1827      * selected text, enforcing the restriction that the start position
1828      * must be greater than or equal to zero.  The end position must be
1829      * greater than or equal to the start position, and less than or
1830      * equal to the length of the text component's text.
1831      * <p>
1832      * If the caller supplies values that are inconsistent or out of
1833      * bounds, the method enforces these constraints silently, and
1834      * without failure. Specifically, if the start position or end
1835      * position is greater than the length of the text, it is reset to
1836      * equal the text length. If the start position is less than zero,
1837      * it is reset to zero, and if the end position is less than the
1838      * start position, it is reset to the start position.
1839      * <p>
1840      * This call is provided for backward compatibility.
1841      * It is routed to a call to <code>setCaretPosition</code>
1842      * followed by a call to <code>moveCaretPosition</code>.
1843      * The preferred way to manage selection is by calling
1844      * those methods directly.
1845      *
1846      * @param selectionStart the start position of the text
1847      * @param selectionEnd the end position of the text
1848      * @see #setCaretPosition
1849      * @see #moveCaretPosition
1850      */
1851     public void select(int selectionStart, int selectionEnd) {
1852         // argument adjustment done by java.awt.TextComponent
1853         int docLength = getDocument().getLength();
1854 
1855         if (selectionStart < 0) {
1856             selectionStart = 0;
1857         }
1858         if (selectionStart > docLength) {
1859             selectionStart = docLength;
1860         }
1861         if (selectionEnd > docLength) {
1862             selectionEnd = docLength;
1863         }
1864         if (selectionEnd < selectionStart) {
1865             selectionEnd = selectionStart;
1866         }
1867 
1868         setCaretPosition(selectionStart);
1869         moveCaretPosition(selectionEnd);
1870     }
1871 
1872     /**
1873      * Selects all the text in the <code>TextComponent</code>.
1874      * Does nothing on a <code>null</code> or empty document.
1875      */
1876     public void selectAll() {
1877         Document doc = getDocument();
1878         if (doc != null) {
1879             setCaretPosition(0);
1880             moveCaretPosition(doc.getLength());
1881         }
1882     }
1883 
1884     // --- Tooltip Methods ---------------------------------------------
1885 
1886     /**
1887      * Returns the string to be used as the tooltip for <code>event</code>.
1888      * This will return one of:
1889      * <ol>
1890      *  <li>If <code>setToolTipText</code> has been invoked with a
1891      *      non-<code>null</code>
1892      *      value, it will be returned, otherwise
1893      *  <li>The value from invoking <code>getToolTipText</code> on
1894      *      the UI will be returned.
1895      * </ol>
1896      * By default <code>JTextComponent</code> does not register
1897      * itself with the <code>ToolTipManager</code>.
1898      * This means that tooltips will NOT be shown from the
1899      * <code>TextUI</code> unless <code>registerComponent</code> has
1900      * been invoked on the <code>ToolTipManager</code>.
1901      *
1902      * @param event the event in question
1903      * @return the string to be used as the tooltip for <code>event</code>
1904      * @see javax.swing.JComponent#setToolTipText
1905      * @see javax.swing.plaf.TextUI#getToolTipText
1906      * @see javax.swing.ToolTipManager#registerComponent
1907      */
1908     public String getToolTipText(MouseEvent event) {
1909         String retValue = super.getToolTipText(event);
1910 
1911         if (retValue == null) {
1912             TextUI ui = getUI();
1913             if (ui != null) {
1914                 retValue = ui.getToolTipText(this, new Point(event.getX(),
1915                                                              event.getY()));
1916             }
1917         }
1918         return retValue;
1919     }
1920 
1921     // --- Scrollable methods ---------------------------------------------
1922 
1923     /**
1924      * Returns the preferred size of the viewport for a view component.
1925      * This is implemented to do the default behavior of returning
1926      * the preferred size of the component.
1927      *
1928      * @return the <code>preferredSize</code> of a <code>JViewport</code>
1929      * whose view is this <code>Scrollable</code>
1930      */
1931     @BeanProperty(bound = false)
1932     public Dimension getPreferredScrollableViewportSize() {
1933         return getPreferredSize();
1934     }
1935 
1936 
1937     /**
1938      * Components that display logical rows or columns should compute
1939      * the scroll increment that will completely expose one new row
1940      * or column, depending on the value of orientation.  Ideally,
1941      * components should handle a partially exposed row or column by
1942      * returning the distance required to completely expose the item.
1943      * <p>
1944      * The default implementation of this is to simply return 10% of
1945      * the visible area.  Subclasses are likely to be able to provide
1946      * a much more reasonable value.
1947      *
1948      * @param visibleRect the view area visible within the viewport
1949      * @param orientation either <code>SwingConstants.VERTICAL</code> or
1950      *   <code>SwingConstants.HORIZONTAL</code>
1951      * @param direction less than zero to scroll up/left, greater than
1952      *   zero for down/right
1953      * @return the "unit" increment for scrolling in the specified direction
1954      * @exception IllegalArgumentException for an invalid orientation
1955      * @see JScrollBar#setUnitIncrement
1956      */
1957     public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
1958         switch(orientation) {
1959         case SwingConstants.VERTICAL:
1960             return visibleRect.height / 10;
1961         case SwingConstants.HORIZONTAL:
1962             return visibleRect.width / 10;
1963         default:
1964             throw new IllegalArgumentException("Invalid orientation: " + orientation);
1965         }
1966     }
1967 
1968 
1969     /**
1970      * Components that display logical rows or columns should compute
1971      * the scroll increment that will completely expose one block
1972      * of rows or columns, depending on the value of orientation.
1973      * <p>
1974      * The default implementation of this is to simply return the visible
1975      * area.  Subclasses will likely be able to provide a much more
1976      * reasonable value.
1977      *
1978      * @param visibleRect the view area visible within the viewport
1979      * @param orientation either <code>SwingConstants.VERTICAL</code> or
1980      *   <code>SwingConstants.HORIZONTAL</code>
1981      * @param direction less than zero to scroll up/left, greater than zero
1982      *  for down/right
1983      * @return the "block" increment for scrolling in the specified direction
1984      * @exception IllegalArgumentException for an invalid orientation
1985      * @see JScrollBar#setBlockIncrement
1986      */
1987     public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
1988         switch(orientation) {
1989         case SwingConstants.VERTICAL:
1990             return visibleRect.height;
1991         case SwingConstants.HORIZONTAL:
1992             return visibleRect.width;
1993         default:
1994             throw new IllegalArgumentException("Invalid orientation: " + orientation);
1995         }
1996     }
1997 
1998 
1999     /**
2000      * Returns true if a viewport should always force the width of this
2001      * <code>Scrollable</code> to match the width of the viewport.
2002      * For example a normal text view that supported line wrapping
2003      * would return true here, since it would be undesirable for
2004      * wrapped lines to disappear beyond the right
2005      * edge of the viewport.  Note that returning true for a
2006      * <code>Scrollable</code> whose ancestor is a <code>JScrollPane</code>
2007      * effectively disables horizontal scrolling.
2008      * <p>
2009      * Scrolling containers, like <code>JViewport</code>,
2010      * will use this method each time they are validated.
2011      *
2012      * @return true if a viewport should force the <code>Scrollable</code>s
2013      *   width to match its own
2014      */
2015     @BeanProperty(bound = false)
2016     public boolean getScrollableTracksViewportWidth() {
2017         Container parent = SwingUtilities.getUnwrappedParent(this);
2018         if (parent instanceof JViewport) {
2019             return parent.getWidth() > getPreferredSize().width;
2020         }
2021         return false;
2022     }
2023 
2024     /**
2025      * Returns true if a viewport should always force the height of this
2026      * <code>Scrollable</code> to match the height of the viewport.
2027      * For example a columnar text view that flowed text in left to
2028      * right columns could effectively disable vertical scrolling by
2029      * returning true here.
2030      * <p>
2031      * Scrolling containers, like <code>JViewport</code>,
2032      * will use this method each time they are validated.
2033      *
2034      * @return true if a viewport should force the Scrollables height
2035      *   to match its own
2036      */
2037     @BeanProperty(bound = false)
2038     public boolean getScrollableTracksViewportHeight() {
2039         Container parent = SwingUtilities.getUnwrappedParent(this);
2040         if (parent instanceof JViewport) {
2041             return parent.getHeight() > getPreferredSize().height;
2042         }
2043         return false;
2044     }
2045 
2046 
2047 //////////////////
2048 // Printing Support
2049 //////////////////
2050 
2051     /**
2052      * A convenience print method that displays a print dialog, and then
2053      * prints this {@code JTextComponent} in <i>interactive</i> mode with no
2054      * header or footer text. Note: this method
2055      * blocks until printing is done.
2056      * <p>
2057      * Note: In <i>headless</i> mode, no dialogs will be shown.
2058      *
2059      * <p> This method calls the full featured
2060      * {@link #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2061      * print} method to perform printing.
2062      * @return {@code true}, unless printing is canceled by the user
2063      * @throws PrinterException if an error in the print system causes the job
2064      *         to be aborted
2065      * @throws SecurityException if this thread is not allowed to
2066      *                           initiate a print job request
2067      *
2068      * @see #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2069      *
2070      * @since 1.6
2071      */
2072 
2073     public boolean print() throws PrinterException {
2074         return print(null, null, true, null, null, true);
2075     }
2076 
2077     /**
2078      * A convenience print method that displays a print dialog, and then
2079      * prints this {@code JTextComponent} in <i>interactive</i> mode with
2080      * the specified header and footer text. Note: this method
2081      * blocks until printing is done.
2082      * <p>
2083      * Note: In <i>headless</i> mode, no dialogs will be shown.
2084      *
2085      * <p> This method calls the full featured
2086      * {@link #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2087      * print} method to perform printing.
2088      * @param headerFormat the text, in {@code MessageFormat}, to be
2089      *        used as the header, or {@code null} for no header
2090      * @param footerFormat the text, in {@code MessageFormat}, to be
2091      *        used as the footer, or {@code null} for no footer
2092      * @return {@code true}, unless printing is canceled by the user
2093      * @throws PrinterException if an error in the print system causes the job
2094      *         to be aborted
2095      * @throws SecurityException if this thread is not allowed to
2096      *                           initiate a print job request
2097      *
2098      * @see #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2099      * @see java.text.MessageFormat
2100      * @since 1.6
2101      */
2102     public boolean print(final MessageFormat headerFormat,
2103             final MessageFormat footerFormat) throws PrinterException {
2104         return print(headerFormat, footerFormat, true, null, null, true);
2105     }
2106 
2107     /**
2108      * Prints the content of this {@code JTextComponent}. Note: this method
2109      * blocks until printing is done.
2110      *
2111      * <p>
2112      * Page header and footer text can be added to the output by providing
2113      * {@code MessageFormat} arguments. The printing code requests
2114      * {@code Strings} from the formats, providing a single item which may be
2115      * included in the formatted string: an {@code Integer} representing the
2116      * current page number.
2117      *
2118      * <p>
2119      * {@code showPrintDialog boolean} parameter allows you to specify whether
2120      * a print dialog is displayed to the user. When it is, the user
2121      * may use the dialog to change printing attributes or even cancel the
2122      * print.
2123      *
2124      * <p>
2125      * {@code service} allows you to provide the initial
2126      * {@code PrintService} for the print dialog, or to specify
2127      * {@code PrintService} to print to when the dialog is not shown.
2128      *
2129      * <p>
2130      * {@code attributes} can be used to provide the
2131      * initial values for the print dialog, or to supply any needed
2132      * attributes when the dialog is not shown. {@code attributes} can
2133      * be used to control how the job will print, for example
2134      * <i>duplex</i> or <i>single-sided</i>.
2135      *
2136      * <p>
2137      * {@code interactive boolean} parameter allows you to specify
2138      * whether to perform printing in <i>interactive</i>
2139      * mode. If {@code true}, a progress dialog, with an abort option,
2140      * is displayed for the duration of printing.  This dialog is
2141      * <i>modal</i> when {@code print} is invoked on the <i>Event Dispatch
2142      * Thread</i> and <i>non-modal</i> otherwise. <b>Warning</b>:
2143      * calling this method on the <i>Event Dispatch Thread</i> with {@code
2144      * interactive false} blocks <i>all</i> events, including repaints, from
2145      * being processed until printing is complete. It is only
2146      * recommended when printing from an application with no
2147      * visible GUI.
2148      *
2149      * <p>
2150      * Note: In <i>headless</i> mode, {@code showPrintDialog} and
2151      * {@code interactive} parameters are ignored and no dialogs are
2152      * shown.
2153      *
2154      * <p>
2155      * This method ensures the {@code document} is not mutated during printing.
2156      * To indicate it visually, {@code setEnabled(false)} is set for the
2157      * duration of printing.
2158      *
2159      * <p>
2160      * This method uses {@link #getPrintable} to render document content.
2161      *
2162      * <p>
2163      * This method is thread-safe, although most Swing methods are not. Please
2164      * see <A
2165      * HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">
2166      * Concurrency in Swing</A> for more information.
2167      *
2168      * <p>
2169      * <b>Sample Usage</b>. This code snippet shows a cross-platform print
2170      * dialog and then prints the {@code JTextComponent} in <i>interactive</i> mode
2171      * unless the user cancels the dialog:
2172      *
2173      * <pre>
2174      * textComponent.print(new MessageFormat(&quot;My text component header&quot;),
2175      *     new MessageFormat(&quot;Footer. Page - {0}&quot;), true, null, null, true);
2176      * </pre>
2177      * <p>
2178      * Executing this code off the <i>Event Dispatch Thread</i>
2179      * performs printing on the <i>background</i>.
2180      * The following pattern might be used for <i>background</i>
2181      * printing:
2182      * <pre>
2183      *     FutureTask&lt;Boolean&gt; future =
2184      *         new FutureTask&lt;Boolean&gt;(
2185      *             new Callable&lt;Boolean&gt;() {
2186      *                 public Boolean call() {
2187      *                     return textComponent.print(.....);
2188      *                 }
2189      *             });
2190      *     executor.execute(future);
2191      * </pre>
2192      *
2193      * @param headerFormat the text, in {@code MessageFormat}, to be
2194      *        used as the header, or {@code null} for no header
2195      * @param footerFormat the text, in {@code MessageFormat}, to be
2196      *        used as the footer, or {@code null} for no footer
2197      * @param showPrintDialog {@code true} to display a print dialog,
2198      *        {@code false} otherwise
2199      * @param service initial {@code PrintService}, or {@code null} for the
2200      *        default
2201      * @param attributes the job attributes to be applied to the print job, or
2202      *        {@code null} for none
2203      * @param interactive whether to print in an interactive mode
2204      * @return {@code true}, unless printing is canceled by the user
2205      * @throws PrinterException if an error in the print system causes the job
2206      *         to be aborted
2207      * @throws SecurityException if this thread is not allowed to
2208      *                           initiate a print job request
2209      *
2210      * @see #getPrintable
2211      * @see java.text.MessageFormat
2212      * @see java.awt.GraphicsEnvironment#isHeadless
2213      * @see java.util.concurrent.FutureTask
2214      *
2215      * @since 1.6
2216      */
2217     public boolean print(final MessageFormat headerFormat,
2218             final MessageFormat footerFormat,
2219             final boolean showPrintDialog,
2220             final PrintService service,
2221             final PrintRequestAttributeSet attributes,
2222             final boolean interactive)
2223             throws PrinterException {
2224 
2225         final PrinterJob job = PrinterJob.getPrinterJob();
2226         final Printable printable;
2227         final PrintingStatus printingStatus;
2228         final boolean isHeadless = GraphicsEnvironment.isHeadless();
2229         final boolean isEventDispatchThread =
2230             SwingUtilities.isEventDispatchThread();
2231         final Printable textPrintable = getPrintable(headerFormat, footerFormat);
2232         if (interactive && ! isHeadless) {
2233             printingStatus =
2234                 PrintingStatus.createPrintingStatus(this, job);
2235             printable =
2236                 printingStatus.createNotificationPrintable(textPrintable);
2237         } else {
2238             printingStatus = null;
2239             printable = textPrintable;
2240         }
2241 
2242         if (service != null) {
2243             job.setPrintService(service);
2244         }
2245 
2246         job.setPrintable(printable);
2247 
2248         final PrintRequestAttributeSet attr = (attributes == null)
2249             ? new HashPrintRequestAttributeSet()
2250             : attributes;
2251 
2252         if (showPrintDialog && ! isHeadless && ! job.printDialog(attr)) {
2253             return false;
2254         }
2255 
2256         /*
2257          * there are three cases for printing:
2258          * 1. print non interactively (! interactive || isHeadless)
2259          * 2. print interactively off EDT
2260          * 3. print interactively on EDT
2261          *
2262          * 1 and 2 prints on the current thread (3 prints on another thread)
2263          * 2 and 3 deal with PrintingStatusDialog
2264          */
2265         final Callable<Object> doPrint =
2266             new Callable<Object>() {
2267                 public Object call() throws Exception {
2268                     try {
2269                         job.print(attr);
2270                     } finally {
2271                         if (printingStatus != null) {
2272                             printingStatus.dispose();
2273                         }
2274                     }
2275                     return null;
2276                 }
2277             };
2278 
2279         final FutureTask<Object> futurePrinting =
2280             new FutureTask<Object>(doPrint);
2281 
2282         final Runnable runnablePrinting =
2283             new Runnable() {
2284                 public void run() {
2285                     //disable component
2286                     boolean wasEnabled = false;
2287                     if (isEventDispatchThread) {
2288                         if (isEnabled()) {
2289                             wasEnabled = true;
2290                             setEnabled(false);
2291                         }
2292                     } else {
2293                         try {
2294                             wasEnabled = SwingUtilities2.submit(
2295                                 new Callable<Boolean>() {
2296                                     public Boolean call() throws Exception {
2297                                         boolean rv = isEnabled();
2298                                         if (rv) {
2299                                             setEnabled(false);
2300                                         }
2301                                         return rv;
2302                                     }
2303                                 }).get();
2304                         } catch (InterruptedException e) {
2305                             throw new RuntimeException(e);
2306                         } catch (ExecutionException e) {
2307                             Throwable cause = e.getCause();
2308                             if (cause instanceof Error) {
2309                                 throw (Error) cause;
2310                             }
2311                             if (cause instanceof RuntimeException) {
2312                                 throw (RuntimeException) cause;
2313                             }
2314                             throw new AssertionError(cause);
2315                         }
2316                     }
2317 
2318                     getDocument().render(futurePrinting);
2319 
2320                     //enable component
2321                     if (wasEnabled) {
2322                         if (isEventDispatchThread) {
2323                             setEnabled(true);
2324                         } else {
2325                             try {
2326                                 SwingUtilities2.submit(
2327                                     new Runnable() {
2328                                         public void run() {
2329                                             setEnabled(true);
2330                                         }
2331                                     }, null).get();
2332                             } catch (InterruptedException e) {
2333                                 throw new RuntimeException(e);
2334                             } catch (ExecutionException e) {
2335                                 Throwable cause = e.getCause();
2336                                 if (cause instanceof Error) {
2337                                     throw (Error) cause;
2338                                 }
2339                                 if (cause instanceof RuntimeException) {
2340                                     throw (RuntimeException) cause;
2341                                 }
2342                                 throw new AssertionError(cause);
2343                             }
2344                         }
2345                     }
2346                 }
2347             };
2348 
2349         if (! interactive || isHeadless) {
2350             runnablePrinting.run();
2351         } else {
2352             if (isEventDispatchThread) {
2353                 (new Thread(runnablePrinting)).start();
2354                 printingStatus.showModal(true);
2355             } else {
2356                 printingStatus.showModal(false);
2357                 runnablePrinting.run();
2358             }
2359         }
2360 
2361         //the printing is done successfully or otherwise.
2362         //dialog is hidden if needed.
2363         try {
2364             futurePrinting.get();
2365         } catch (InterruptedException e) {
2366             throw new RuntimeException(e);
2367         } catch (ExecutionException e) {
2368             Throwable cause = e.getCause();
2369             if (cause instanceof PrinterAbortException) {
2370                 if (printingStatus != null
2371                     && printingStatus.isAborted()) {
2372                     return false;
2373                 } else {
2374                     throw (PrinterAbortException) cause;
2375                 }
2376             } else if (cause instanceof PrinterException) {
2377                 throw (PrinterException) cause;
2378             } else if (cause instanceof RuntimeException) {
2379                 throw (RuntimeException) cause;
2380             } else if (cause instanceof Error) {
2381                 throw (Error) cause;
2382             } else {
2383                 throw new AssertionError(cause);
2384             }
2385         }
2386         return true;
2387     }
2388 
2389 
2390     /**
2391      * Returns a {@code Printable} to use for printing the content of this
2392      * {@code JTextComponent}. The returned {@code Printable} prints
2393      * the document as it looks on the screen except being reformatted
2394      * to fit the paper.
2395      * The returned {@code Printable} can be wrapped inside another
2396      * {@code Printable} in order to create complex reports and
2397      * documents.
2398      *
2399      *
2400      * <p>
2401      * The returned {@code Printable} shares the {@code document} with this
2402      * {@code JTextComponent}. It is the responsibility of the developer to
2403      * ensure that the {@code document} is not mutated while this {@code Printable}
2404      * is used. Printing behavior is undefined when the {@code document} is
2405      * mutated during printing.
2406      *
2407      * <p>
2408      * Page header and footer text can be added to the output by providing
2409      * {@code MessageFormat} arguments. The printing code requests
2410      * {@code Strings} from the formats, providing a single item which may be
2411      * included in the formatted string: an {@code Integer} representing the
2412      * current page number.
2413      *
2414      * <p>
2415      * The returned {@code Printable} when printed, formats the
2416      * document content appropriately for the page size. For correct
2417      * line wrapping the {@code imageable width} of all pages must be the
2418      * same. See {@link java.awt.print.PageFormat#getImageableWidth}.
2419      *
2420      * <p>
2421      * This method is thread-safe, although most Swing methods are not. Please
2422      * see <A
2423      * HREF="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html">
2424      * Concurrency in Swing</A> for more information.
2425      *
2426      * <p>
2427      * The returned {@code Printable} can be printed on any thread.
2428      *
2429      * <p>
2430      * This implementation returned {@code Printable} performs all painting on
2431      * the <i>Event Dispatch Thread</i>, regardless of what thread it is
2432      * used on.
2433      *
2434      * @param headerFormat the text, in {@code MessageFormat}, to be
2435      *        used as the header, or {@code null} for no header
2436      * @param footerFormat the text, in {@code MessageFormat}, to be
2437      *        used as the footer, or {@code null} for no footer
2438      * @return a {@code Printable} for use in printing content of this
2439      *         {@code JTextComponent}
2440      *
2441      *
2442      * @see java.awt.print.Printable
2443      * @see java.awt.print.PageFormat
2444      * @see javax.swing.text.Document#render(java.lang.Runnable)
2445      *
2446      * @since 1.6
2447      */
2448     public Printable getPrintable(final MessageFormat headerFormat,
2449                                   final MessageFormat footerFormat) {
2450         return TextComponentPrintable.getPrintable(
2451                    this, headerFormat, footerFormat);
2452     }
2453 
2454 
2455 /////////////////
2456 // Accessibility support
2457 ////////////////
2458 
2459 
2460     /**
2461      * Gets the <code>AccessibleContext</code> associated with this
2462      * <code>JTextComponent</code>. For text components,
2463      * the <code>AccessibleContext</code> takes the form of an
2464      * <code>AccessibleJTextComponent</code>.
2465      * A new <code>AccessibleJTextComponent</code> instance
2466      * is created if necessary.
2467      *
2468      * @return an <code>AccessibleJTextComponent</code> that serves as the
2469      *         <code>AccessibleContext</code> of this
2470      *         <code>JTextComponent</code>
2471      */
2472     @BeanProperty(bound = false)
2473     public AccessibleContext getAccessibleContext() {
2474         if (accessibleContext == null) {
2475             accessibleContext = new AccessibleJTextComponent();
2476         }
2477         return accessibleContext;
2478     }
2479 
2480     /**
2481      * This class implements accessibility support for the
2482      * <code>JTextComponent</code> class.  It provides an implementation of
2483      * the Java Accessibility API appropriate to menu user-interface elements.
2484      * <p>
2485      * <strong>Warning:</strong>
2486      * Serialized objects of this class will not be compatible with
2487      * future Swing releases. The current serialization support is
2488      * appropriate for short term storage or RMI between applications running
2489      * the same version of Swing.  As of 1.4, support for long term storage
2490      * of all JavaBeans&trade;
2491      * has been added to the <code>java.beans</code> package.
2492      * Please see {@link java.beans.XMLEncoder}.
2493      */
2494     @SuppressWarnings("serial") // Same-version serialization only
2495     public class AccessibleJTextComponent extends AccessibleJComponent
2496     implements AccessibleText, CaretListener, DocumentListener,
2497                AccessibleAction, AccessibleEditableText,
2498                AccessibleExtendedText {
2499 
2500         int caretPos;
2501         Point oldLocationOnScreen;
2502 
2503         /**
2504          * Constructs an AccessibleJTextComponent.  Adds a listener to track
2505          * caret change.
2506          */
2507         public AccessibleJTextComponent() {
2508             Document doc = JTextComponent.this.getDocument();
2509             if (doc != null) {
2510                 doc.addDocumentListener(this);
2511             }
2512             JTextComponent.this.addCaretListener(this);
2513             caretPos = getCaretPosition();
2514 
2515             try {
2516                 oldLocationOnScreen = getLocationOnScreen();
2517             } catch (IllegalComponentStateException iae) {
2518             }
2519 
2520             // Fire a ACCESSIBLE_VISIBLE_DATA_PROPERTY PropertyChangeEvent
2521             // when the text component moves (e.g., when scrolling).
2522             // Using an anonymous class since making AccessibleJTextComponent
2523             // implement ComponentListener would be an API change.
2524             JTextComponent.this.addComponentListener(new ComponentAdapter() {
2525 
2526                 public void componentMoved(ComponentEvent e) {
2527                     try {
2528                         Point newLocationOnScreen = getLocationOnScreen();
2529                         firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2530                                            oldLocationOnScreen,
2531                                            newLocationOnScreen);
2532 
2533                         oldLocationOnScreen = newLocationOnScreen;
2534                     } catch (IllegalComponentStateException iae) {
2535                     }
2536                 }
2537             });
2538         }
2539 
2540         /**
2541          * Handles caret updates (fire appropriate property change event,
2542          * which are AccessibleContext.ACCESSIBLE_CARET_PROPERTY and
2543          * AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY).
2544          * This keeps track of the dot position internally.  When the caret
2545          * moves, the internal position is updated after firing the event.
2546          *
2547          * @param e the CaretEvent
2548          */
2549         public void caretUpdate(CaretEvent e) {
2550             int dot = e.getDot();
2551             int mark = e.getMark();
2552             if (caretPos != dot) {
2553                 // the caret moved
2554                 firePropertyChange(ACCESSIBLE_CARET_PROPERTY,
2555                     caretPos, dot);
2556                 caretPos = dot;
2557 
2558                 try {
2559                     oldLocationOnScreen = getLocationOnScreen();
2560                 } catch (IllegalComponentStateException iae) {
2561                 }
2562             }
2563             if (mark != dot) {
2564                 // there is a selection
2565                 firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
2566                     getSelectedText());
2567             }
2568         }
2569 
2570         // DocumentListener methods
2571 
2572         /**
2573          * Handles document insert (fire appropriate property change event
2574          * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
2575          * This tracks the changed offset via the event.
2576          *
2577          * @param e the DocumentEvent
2578          */
2579         public void insertUpdate(DocumentEvent e) {
2580             final Integer pos = new Integer (e.getOffset());
2581             if (SwingUtilities.isEventDispatchThread()) {
2582                 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
2583             } else {
2584                 Runnable doFire = new Runnable() {
2585                     public void run() {
2586                         firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
2587                                            null, pos);
2588                     }
2589                 };
2590                 SwingUtilities.invokeLater(doFire);
2591             }
2592         }
2593 
2594         /**
2595          * Handles document remove (fire appropriate property change event,
2596          * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
2597          * This tracks the changed offset via the event.
2598          *
2599          * @param e the DocumentEvent
2600          */
2601         public void removeUpdate(DocumentEvent e) {
2602             final Integer pos = new Integer (e.getOffset());
2603             if (SwingUtilities.isEventDispatchThread()) {
2604                 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
2605             } else {
2606                 Runnable doFire = new Runnable() {
2607                     public void run() {
2608                         firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
2609                                            null, pos);
2610                     }
2611                 };
2612                 SwingUtilities.invokeLater(doFire);
2613             }
2614         }
2615 
2616         /**
2617          * Handles document remove (fire appropriate property change event,
2618          * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
2619          * This tracks the changed offset via the event.
2620          *
2621          * @param e the DocumentEvent
2622          */
2623         public void changedUpdate(DocumentEvent e) {
2624             final Integer pos = new Integer (e.getOffset());
2625             if (SwingUtilities.isEventDispatchThread()) {
2626                 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
2627             } else {
2628                 Runnable doFire = new Runnable() {
2629                     public void run() {
2630                         firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
2631                                            null, pos);
2632                     }
2633                 };
2634                 SwingUtilities.invokeLater(doFire);
2635             }
2636         }
2637 
2638         /**
2639          * Gets the state set of the JTextComponent.
2640          * The AccessibleStateSet of an object is composed of a set of
2641          * unique AccessibleState's.  A change in the AccessibleStateSet
2642          * of an object will cause a PropertyChangeEvent to be fired
2643          * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
2644          *
2645          * @return an instance of AccessibleStateSet containing the
2646          * current state set of the object
2647          * @see AccessibleStateSet
2648          * @see AccessibleState
2649          * @see #addPropertyChangeListener
2650          */
2651         public AccessibleStateSet getAccessibleStateSet() {
2652             AccessibleStateSet states = super.getAccessibleStateSet();
2653             if (JTextComponent.this.isEditable()) {
2654                 states.add(AccessibleState.EDITABLE);
2655             }
2656             return states;
2657         }
2658 
2659 
2660         /**
2661          * Gets the role of this object.
2662          *
2663          * @return an instance of AccessibleRole describing the role of the
2664          * object (AccessibleRole.TEXT)
2665          * @see AccessibleRole
2666          */
2667         public AccessibleRole getAccessibleRole() {
2668             return AccessibleRole.TEXT;
2669         }
2670 
2671         /**
2672          * Get the AccessibleText associated with this object.  In the
2673          * implementation of the Java Accessibility API for this class,
2674          * return this object, which is responsible for implementing the
2675          * AccessibleText interface on behalf of itself.
2676          *
2677          * @return this object
2678          */
2679         public AccessibleText getAccessibleText() {
2680             return this;
2681         }
2682 
2683 
2684         // --- interface AccessibleText methods ------------------------
2685 
2686         /**
2687          * Many of these methods are just convenience methods; they
2688          * just call the equivalent on the parent
2689          */
2690 
2691         /**
2692          * Given a point in local coordinates, return the zero-based index
2693          * of the character under that Point.  If the point is invalid,
2694          * this method returns -1.
2695          *
2696          * @param p the Point in local coordinates
2697          * @return the zero-based index of the character under Point p.
2698          */
2699         public int getIndexAtPoint(Point p) {
2700             if (p == null) {
2701                 return -1;
2702             }
2703             return JTextComponent.this.viewToModel(p);
2704         }
2705 
2706             /**
2707              * Gets the editor's drawing rectangle.  Stolen
2708              * from the unfortunately named
2709              * BasicTextUI.getVisibleEditorRect()
2710              *
2711              * @return the bounding box for the root view
2712              */
2713             Rectangle getRootEditorRect() {
2714                 Rectangle alloc = JTextComponent.this.getBounds();
2715                 if ((alloc.width > 0) && (alloc.height > 0)) {
2716                         alloc.x = alloc.y = 0;
2717                         Insets insets = JTextComponent.this.getInsets();
2718                         alloc.x += insets.left;
2719                         alloc.y += insets.top;
2720                         alloc.width -= insets.left + insets.right;
2721                         alloc.height -= insets.top + insets.bottom;
2722                         return alloc;
2723                 }
2724                 return null;
2725             }
2726 
2727         /**
2728          * Determines the bounding box of the character at the given
2729          * index into the string.  The bounds are returned in local
2730          * coordinates.  If the index is invalid a null rectangle
2731          * is returned.
2732          *
2733          * The screen coordinates returned are "unscrolled coordinates"
2734          * if the JTextComponent is contained in a JScrollPane in which
2735          * case the resulting rectangle should be composed with the parent
2736          * coordinates.  A good algorithm to use is:
2737          * <pre>
2738          * Accessible a:
2739          * AccessibleText at = a.getAccessibleText();
2740          * AccessibleComponent ac = a.getAccessibleComponent();
2741          * Rectangle r = at.getCharacterBounds();
2742          * Point p = ac.getLocation();
2743          * r.x += p.x;
2744          * r.y += p.y;
2745          * </pre>
2746          *
2747          * Note: the JTextComponent must have a valid size (e.g. have
2748          * been added to a parent container whose ancestor container
2749          * is a valid top-level window) for this method to be able
2750          * to return a meaningful (non-null) value.
2751          *
2752          * @param i the index into the String &ge; 0
2753          * @return the screen coordinates of the character's bounding box
2754          */
2755         public Rectangle getCharacterBounds(int i) {
2756             if (i < 0 || i > model.getLength()-1) {
2757                 return null;
2758             }
2759             TextUI ui = getUI();
2760             if (ui == null) {
2761                 return null;
2762             }
2763             Rectangle rect = null;
2764             Rectangle alloc = getRootEditorRect();
2765             if (alloc == null) {
2766                 return null;
2767             }
2768             if (model instanceof AbstractDocument) {
2769                 ((AbstractDocument)model).readLock();
2770             }
2771             try {
2772                 View rootView = ui.getRootView(JTextComponent.this);
2773                 if (rootView != null) {
2774                     rootView.setSize(alloc.width, alloc.height);
2775 
2776                     Shape bounds = rootView.modelToView(i,
2777                                     Position.Bias.Forward, i+1,
2778                                     Position.Bias.Backward, alloc);
2779 
2780                     rect = (bounds instanceof Rectangle) ?
2781                      (Rectangle)bounds : bounds.getBounds();
2782 
2783                 }
2784             } catch (BadLocationException e) {
2785             } finally {
2786                 if (model instanceof AbstractDocument) {
2787                     ((AbstractDocument)model).readUnlock();
2788                 }
2789             }
2790             return rect;
2791         }
2792 
2793         /**
2794          * Returns the number of characters (valid indices)
2795          *
2796          * @return the number of characters &ge; 0
2797          */
2798         public int getCharCount() {
2799             return model.getLength();
2800         }
2801 
2802         /**
2803          * Returns the zero-based offset of the caret.
2804          *
2805          * Note: The character to the right of the caret will have the
2806          * same index value as the offset (the caret is between
2807          * two characters).
2808          *
2809          * @return the zero-based offset of the caret.
2810          */
2811         public int getCaretPosition() {
2812             return JTextComponent.this.getCaretPosition();
2813         }
2814 
2815         /**
2816          * Returns the AttributeSet for a given character (at a given index).
2817          *
2818          * @param i the zero-based index into the text
2819          * @return the AttributeSet of the character
2820          */
2821         public AttributeSet getCharacterAttribute(int i) {
2822             Element e = null;
2823             if (model instanceof AbstractDocument) {
2824                 ((AbstractDocument)model).readLock();
2825             }
2826             try {
2827                 for (e = model.getDefaultRootElement(); ! e.isLeaf(); ) {
2828                     int index = e.getElementIndex(i);
2829                     e = e.getElement(index);
2830                 }
2831             } finally {
2832                 if (model instanceof AbstractDocument) {
2833                     ((AbstractDocument)model).readUnlock();
2834                 }
2835             }
2836             return e.getAttributes();
2837         }
2838 
2839 
2840         /**
2841          * Returns the start offset within the selected text.
2842          * If there is no selection, but there is
2843          * a caret, the start and end offsets will be the same.
2844          * Return 0 if the text is empty, or the caret position
2845          * if no selection.
2846          *
2847          * @return the index into the text of the start of the selection &ge; 0
2848          */
2849         public int getSelectionStart() {
2850             return JTextComponent.this.getSelectionStart();
2851         }
2852 
2853         /**
2854          * Returns the end offset within the selected text.
2855          * If there is no selection, but there is
2856          * a caret, the start and end offsets will be the same.
2857          * Return 0 if the text is empty, or the caret position
2858          * if no selection.
2859          *
2860          * @return the index into the text of the end of the selection &ge; 0
2861          */
2862         public int getSelectionEnd() {
2863             return JTextComponent.this.getSelectionEnd();
2864         }
2865 
2866         /**
2867          * Returns the portion of the text that is selected.
2868          *
2869          * @return the text, null if no selection
2870          */
2871         public String getSelectedText() {
2872             return JTextComponent.this.getSelectedText();
2873         }
2874 
2875        /**
2876          * IndexedSegment extends Segment adding the offset into the
2877          * the model the <code>Segment</code> was asked for.
2878          */
2879         private class IndexedSegment extends Segment {
2880             /**
2881              * Offset into the model that the position represents.
2882              */
2883             public int modelOffset;
2884         }
2885 
2886 
2887         // TIGER - 4170173
2888         /**
2889          * Returns the String at a given index. Whitespace
2890          * between words is treated as a word.
2891          *
2892          * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2893          * @param index an index within the text
2894          * @return the letter, word, or sentence.
2895          *
2896          */
2897         public String getAtIndex(int part, int index) {
2898             return getAtIndex(part, index, 0);
2899         }
2900 
2901 
2902         /**
2903          * Returns the String after a given index. Whitespace
2904          * between words is treated as a word.
2905          *
2906          * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2907          * @param index an index within the text
2908          * @return the letter, word, or sentence.
2909          */
2910         public String getAfterIndex(int part, int index) {
2911             return getAtIndex(part, index, 1);
2912         }
2913 
2914 
2915         /**
2916          * Returns the String before a given index. Whitespace
2917          * between words is treated a word.
2918          *
2919          * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2920          * @param index an index within the text
2921          * @return the letter, word, or sentence.
2922          */
2923         public String getBeforeIndex(int part, int index) {
2924             return getAtIndex(part, index, -1);
2925         }
2926 
2927 
2928         /**
2929          * Gets the word, sentence, or character at <code>index</code>.
2930          * If <code>direction</code> is non-null this will find the
2931          * next/previous word/sentence/character.
2932          */
2933         private String getAtIndex(int part, int index, int direction) {
2934             if (model instanceof AbstractDocument) {
2935                 ((AbstractDocument)model).readLock();
2936             }
2937             try {
2938                 if (index < 0 || index >= model.getLength()) {
2939                     return null;
2940                 }
2941                 switch (part) {
2942                 case AccessibleText.CHARACTER:
2943                     if (index + direction < model.getLength() &&
2944                         index + direction >= 0) {
2945                         return model.getText(index + direction, 1);
2946                     }
2947                     break;
2948 
2949 
2950                 case AccessibleText.WORD:
2951                 case AccessibleText.SENTENCE:
2952                     IndexedSegment seg = getSegmentAt(part, index);
2953                     if (seg != null) {
2954                         if (direction != 0) {
2955                             int next;
2956 
2957 
2958                             if (direction < 0) {
2959                                 next = seg.modelOffset - 1;
2960                             }
2961                             else {
2962                                 next = seg.modelOffset + direction * seg.count;
2963                             }
2964                             if (next >= 0 && next <= model.getLength()) {
2965                                 seg = getSegmentAt(part, next);
2966                             }
2967                             else {
2968                                 seg = null;
2969                             }
2970                         }
2971                         if (seg != null) {
2972                             return new String(seg.array, seg.offset,
2973                                                   seg.count);
2974                         }
2975                     }
2976                     break;
2977 
2978 
2979                 default:
2980                     break;
2981                 }
2982             } catch (BadLocationException e) {
2983             } finally {
2984                 if (model instanceof AbstractDocument) {
2985                     ((AbstractDocument)model).readUnlock();
2986                 }
2987             }
2988             return null;
2989         }
2990 
2991 
2992         /*
2993          * Returns the paragraph element for the specified index.
2994          */
2995         private Element getParagraphElement(int index) {
2996             if (model instanceof PlainDocument ) {
2997                 PlainDocument sdoc = (PlainDocument)model;
2998                 return sdoc.getParagraphElement(index);
2999             } else if (model instanceof StyledDocument) {
3000                 StyledDocument sdoc = (StyledDocument)model;
3001                 return sdoc.getParagraphElement(index);
3002             } else {
3003                 Element para;
3004                 for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) {
3005                     int pos = para.getElementIndex(index);
3006                     para = para.getElement(pos);
3007                 }
3008                 if (para == null) {
3009                     return null;
3010                 }
3011                 return para.getParentElement();
3012             }
3013         }
3014 
3015         /*
3016          * Returns a <code>Segment</code> containing the paragraph text
3017          * at <code>index</code>, or null if <code>index</code> isn't
3018          * valid.
3019          */
3020         private IndexedSegment getParagraphElementText(int index)
3021                                   throws BadLocationException {
3022             Element para = getParagraphElement(index);
3023 
3024 
3025             if (para != null) {
3026                 IndexedSegment segment = new IndexedSegment();
3027                 try {
3028                     int length = para.getEndOffset() - para.getStartOffset();
3029                     model.getText(para.getStartOffset(), length, segment);
3030                 } catch (BadLocationException e) {
3031                     return null;
3032                 }
3033                 segment.modelOffset = para.getStartOffset();
3034                 return segment;
3035             }
3036             return null;
3037         }
3038 
3039 
3040         /**
3041          * Returns the Segment at <code>index</code> representing either
3042          * the paragraph or sentence as identified by <code>part</code>, or
3043          * null if a valid paragraph/sentence can't be found. The offset
3044          * will point to the start of the word/sentence in the array, and
3045          * the modelOffset will point to the location of the word/sentence
3046          * in the model.
3047          */
3048         private IndexedSegment getSegmentAt(int part, int index) throws
3049                                   BadLocationException {
3050             IndexedSegment seg = getParagraphElementText(index);
3051             if (seg == null) {
3052                 return null;
3053             }
3054             BreakIterator iterator;
3055             switch (part) {
3056             case AccessibleText.WORD:
3057                 iterator = BreakIterator.getWordInstance(getLocale());
3058                 break;
3059             case AccessibleText.SENTENCE:
3060                 iterator = BreakIterator.getSentenceInstance(getLocale());
3061                 break;
3062             default:
3063                 return null;
3064             }
3065             seg.first();
3066             iterator.setText(seg);
3067             int end = iterator.following(index - seg.modelOffset + seg.offset);
3068             if (end == BreakIterator.DONE) {
3069                 return null;
3070             }
3071             if (end > seg.offset + seg.count) {
3072                 return null;
3073             }
3074             int begin = iterator.previous();
3075             if (begin == BreakIterator.DONE ||
3076                          begin >= seg.offset + seg.count) {
3077                 return null;
3078             }
3079             seg.modelOffset = seg.modelOffset + begin - seg.offset;
3080             seg.offset = begin;
3081             seg.count = end - begin;
3082             return seg;
3083         }
3084 
3085         // begin AccessibleEditableText methods -----
3086 
3087         /**
3088          * Returns the AccessibleEditableText interface for
3089          * this text component.
3090          *
3091          * @return the AccessibleEditableText interface
3092          * @since 1.4
3093          */
3094         public AccessibleEditableText getAccessibleEditableText() {
3095             return this;
3096         }
3097 
3098         /**
3099          * Sets the text contents to the specified string.
3100          *
3101          * @param s the string to set the text contents
3102          * @since 1.4
3103          */
3104         public void setTextContents(String s) {
3105             JTextComponent.this.setText(s);
3106         }
3107 
3108         /**
3109          * Inserts the specified string at the given index
3110          *
3111          * @param index the index in the text where the string will
3112          * be inserted
3113          * @param s the string to insert in the text
3114          * @since 1.4
3115          */
3116         public void insertTextAtIndex(int index, String s) {
3117             Document doc = JTextComponent.this.getDocument();
3118             if (doc != null) {
3119                 try {
3120                     if (s != null && s.length() > 0) {
3121                         boolean composedTextSaved = saveComposedText(index);
3122                         doc.insertString(index, s, null);
3123                         if (composedTextSaved) {
3124                             restoreComposedText();
3125                         }
3126                     }
3127                 } catch (BadLocationException e) {
3128                     UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
3129                 }
3130             }
3131         }
3132 
3133         /**
3134          * Returns the text string between two indices.
3135          *
3136          * @param startIndex the starting index in the text
3137          * @param endIndex the ending index in the text
3138          * @return the text string between the indices
3139          * @since 1.4
3140          */
3141         public String getTextRange(int startIndex, int endIndex) {
3142             String txt = null;
3143             int p0 = Math.min(startIndex, endIndex);
3144             int p1 = Math.max(startIndex, endIndex);
3145             if (p0 != p1) {
3146                 try {
3147                     Document doc = JTextComponent.this.getDocument();
3148                     txt = doc.getText(p0, p1 - p0);
3149                 } catch (BadLocationException e) {
3150                     throw new IllegalArgumentException(e.getMessage());
3151                 }
3152             }
3153             return txt;
3154         }
3155 
3156         /**
3157          * Deletes the text between two indices
3158          *
3159          * @param startIndex the starting index in the text
3160          * @param endIndex the ending index in the text
3161          * @since 1.4
3162          */
3163         public void delete(int startIndex, int endIndex) {
3164             if (isEditable() && isEnabled()) {
3165                 try {
3166                     int p0 = Math.min(startIndex, endIndex);
3167                     int p1 = Math.max(startIndex, endIndex);
3168                     if (p0 != p1) {
3169                         Document doc = getDocument();
3170                         doc.remove(p0, p1 - p0);
3171                     }
3172                 } catch (BadLocationException e) {
3173                 }
3174             } else {
3175                 UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
3176             }
3177         }
3178 
3179         /**
3180          * Cuts the text between two indices into the system clipboard.
3181          *
3182          * @param startIndex the starting index in the text
3183          * @param endIndex the ending index in the text
3184          * @since 1.4
3185          */
3186         public void cut(int startIndex, int endIndex) {
3187             selectText(startIndex, endIndex);
3188             JTextComponent.this.cut();
3189         }
3190 
3191         /**
3192          * Pastes the text from the system clipboard into the text
3193          * starting at the specified index.
3194          *
3195          * @param startIndex the starting index in the text
3196          * @since 1.4
3197          */
3198         public void paste(int startIndex) {
3199             setCaretPosition(startIndex);
3200             JTextComponent.this.paste();
3201         }
3202 
3203         /**
3204          * Replaces the text between two indices with the specified
3205          * string.
3206          *
3207          * @param startIndex the starting index in the text
3208          * @param endIndex the ending index in the text
3209          * @param s the string to replace the text between two indices
3210          * @since 1.4
3211          */
3212         public void replaceText(int startIndex, int endIndex, String s) {
3213             selectText(startIndex, endIndex);
3214             JTextComponent.this.replaceSelection(s);
3215         }
3216 
3217         /**
3218          * Selects the text between two indices.
3219          *
3220          * @param startIndex the starting index in the text
3221          * @param endIndex the ending index in the text
3222          * @since 1.4
3223          */
3224         public void selectText(int startIndex, int endIndex) {
3225             JTextComponent.this.select(startIndex, endIndex);
3226         }
3227 
3228         /**
3229          * Sets attributes for the text between two indices.
3230          *
3231          * @param startIndex the starting index in the text
3232          * @param endIndex the ending index in the text
3233          * @param as the attribute set
3234          * @see AttributeSet
3235          * @since 1.4
3236          */
3237         public void setAttributes(int startIndex, int endIndex,
3238             AttributeSet as) {
3239 
3240             // Fixes bug 4487492
3241             Document doc = JTextComponent.this.getDocument();
3242             if (doc != null && doc instanceof StyledDocument) {
3243                 StyledDocument sDoc = (StyledDocument)doc;
3244                 int offset = startIndex;
3245                 int length = endIndex - startIndex;
3246                 sDoc.setCharacterAttributes(offset, length, as, true);
3247             }
3248         }
3249 
3250         // ----- end AccessibleEditableText methods
3251 
3252 
3253         // ----- begin AccessibleExtendedText methods
3254 
3255 // Probably should replace the helper method getAtIndex() to return
3256 // instead an AccessibleTextSequence also for LINE & ATTRIBUTE_RUN
3257 // and then make the AccessibleText methods get[At|After|Before]Point
3258 // call this new method instead and return only the string portion
3259 
3260         /**
3261          * Returns the AccessibleTextSequence at a given <code>index</code>.
3262          * If <code>direction</code> is non-null this will find the
3263          * next/previous word/sentence/character.
3264          *
3265          * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3266          * <code>SENTENCE</code>, <code>LINE</code> or
3267          * <code>ATTRIBUTE_RUN</code> to retrieve
3268          * @param index an index within the text
3269          * @param direction is either -1, 0, or 1
3270          * @return an <code>AccessibleTextSequence</code> specifying the text
3271          * if <code>part</code> and <code>index</code> are valid.  Otherwise,
3272          * <code>null</code> is returned.
3273          *
3274          * @see javax.accessibility.AccessibleText#CHARACTER
3275          * @see javax.accessibility.AccessibleText#WORD
3276          * @see javax.accessibility.AccessibleText#SENTENCE
3277          * @see javax.accessibility.AccessibleExtendedText#LINE
3278          * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3279          *
3280          * @since 1.6
3281          */
3282         private AccessibleTextSequence getSequenceAtIndex(int part,
3283             int index, int direction) {
3284             if (index < 0 || index >= model.getLength()) {
3285                 return null;
3286             }
3287             if (direction < -1 || direction > 1) {
3288                 return null;    // direction must be 1, 0, or -1
3289             }
3290 
3291             switch (part) {
3292             case AccessibleText.CHARACTER:
3293                 if (model instanceof AbstractDocument) {
3294                     ((AbstractDocument)model).readLock();
3295                 }
3296                 AccessibleTextSequence charSequence = null;
3297                 try {
3298                     if (index + direction < model.getLength() &&
3299                         index + direction >= 0) {
3300                         charSequence =
3301                             new AccessibleTextSequence(index + direction,
3302                             index + direction + 1,
3303                             model.getText(index + direction, 1));
3304                     }
3305 
3306                 } catch (BadLocationException e) {
3307                     // we are intentionally silent; our contract says we return
3308                     // null if there is any failure in this method
3309                 } finally {
3310                     if (model instanceof AbstractDocument) {
3311                         ((AbstractDocument)model).readUnlock();
3312                     }
3313                 }
3314                 return charSequence;
3315 
3316             case AccessibleText.WORD:
3317             case AccessibleText.SENTENCE:
3318                 if (model instanceof AbstractDocument) {
3319                     ((AbstractDocument)model).readLock();
3320                 }
3321                 AccessibleTextSequence rangeSequence = null;
3322                 try {
3323                     IndexedSegment seg = getSegmentAt(part, index);
3324                     if (seg != null) {
3325                         if (direction != 0) {
3326                             int next;
3327 
3328                             if (direction < 0) {
3329                                 next = seg.modelOffset - 1;
3330                             }
3331                             else {
3332                                 next = seg.modelOffset + seg.count;
3333                             }
3334                             if (next >= 0 && next <= model.getLength()) {
3335                                 seg = getSegmentAt(part, next);
3336                             }
3337                             else {
3338                                 seg = null;
3339                             }
3340                         }
3341                         if (seg != null &&
3342                             (seg.offset + seg.count) <= model.getLength()) {
3343                             rangeSequence =
3344                                 new AccessibleTextSequence (seg.offset,
3345                                 seg.offset + seg.count,
3346                                 new String(seg.array, seg.offset, seg.count));
3347                         } // else we leave rangeSequence set to null
3348                     }
3349                 } catch(BadLocationException e) {
3350                     // we are intentionally silent; our contract says we return
3351                     // null if there is any failure in this method
3352                 } finally {
3353                     if (model instanceof AbstractDocument) {
3354                         ((AbstractDocument)model).readUnlock();
3355                     }
3356                 }
3357                 return rangeSequence;
3358 
3359             case AccessibleExtendedText.LINE:
3360                 AccessibleTextSequence lineSequence = null;
3361                 if (model instanceof AbstractDocument) {
3362                     ((AbstractDocument)model).readLock();
3363                 }
3364                 try {
3365                     int startIndex =
3366                         Utilities.getRowStart(JTextComponent.this, index);
3367                     int endIndex =
3368                         Utilities.getRowEnd(JTextComponent.this, index);
3369                     if (startIndex >= 0 && endIndex >= startIndex) {
3370                         if (direction == 0) {
3371                             lineSequence =
3372                                 new AccessibleTextSequence(startIndex, endIndex,
3373                                     model.getText(startIndex,
3374                                         endIndex - startIndex + 1));
3375                         } else if (direction == -1 && startIndex > 0) {
3376                             endIndex =
3377                                 Utilities.getRowEnd(JTextComponent.this,
3378                                     startIndex - 1);
3379                             startIndex =
3380                                 Utilities.getRowStart(JTextComponent.this,
3381                                     startIndex - 1);
3382                             if (startIndex >= 0 && endIndex >= startIndex) {
3383                                 lineSequence =
3384                                     new AccessibleTextSequence(startIndex,
3385                                         endIndex,
3386                                         model.getText(startIndex,
3387                                             endIndex - startIndex + 1));
3388                             }
3389                         } else if (direction == 1 &&
3390                          endIndex < model.getLength()) {
3391                             startIndex =
3392                                 Utilities.getRowStart(JTextComponent.this,
3393                                     endIndex + 1);
3394                             endIndex =
3395                                 Utilities.getRowEnd(JTextComponent.this,
3396                                     endIndex + 1);
3397                             if (startIndex >= 0 && endIndex >= startIndex) {
3398                                 lineSequence =
3399                                     new AccessibleTextSequence(startIndex,
3400                                         endIndex, model.getText(startIndex,
3401                                             endIndex - startIndex + 1));
3402                             }
3403                         }
3404                         // already validated 'direction' above...
3405                     }
3406                 } catch(BadLocationException e) {
3407                     // we are intentionally silent; our contract says we return
3408                     // null if there is any failure in this method
3409                 } finally {
3410                     if (model instanceof AbstractDocument) {
3411                         ((AbstractDocument)model).readUnlock();
3412                     }
3413                 }
3414                 return lineSequence;
3415 
3416             case AccessibleExtendedText.ATTRIBUTE_RUN:
3417                 // assumptions: (1) that all characters in a single element
3418                 // share the same attribute set; (2) that adjacent elements
3419                 // *may* share the same attribute set
3420 
3421                 int attributeRunStartIndex, attributeRunEndIndex;
3422                 String runText = null;
3423                 if (model instanceof AbstractDocument) {
3424                     ((AbstractDocument)model).readLock();
3425                 }
3426 
3427                 try {
3428                     attributeRunStartIndex = attributeRunEndIndex =
3429                      Integer.MIN_VALUE;
3430                     int tempIndex = index;
3431                     switch (direction) {
3432                     case -1:
3433                         // going backwards, so find left edge of this run -
3434                         // that'll be the end of the previous run
3435                         // (off-by-one counting)
3436                         attributeRunEndIndex = getRunEdge(index, direction);
3437                         // now set ourselves up to find the left edge of the
3438                         // prev. run
3439                         tempIndex = attributeRunEndIndex - 1;
3440                         break;
3441                     case 1:
3442                         // going forward, so find right edge of this run -
3443                         // that'll be the start of the next run
3444                         // (off-by-one counting)
3445                         attributeRunStartIndex = getRunEdge(index, direction);
3446                         // now set ourselves up to find the right edge of the
3447                         // next run
3448                         tempIndex = attributeRunStartIndex;
3449                         break;
3450                     case 0:
3451                         // interested in the current run, so nothing special to
3452                         // set up in advance...
3453                         break;
3454                     default:
3455                         // only those three values of direction allowed...
3456                         throw new AssertionError(direction);
3457                     }
3458 
3459                     // set the unset edge; if neither set then we're getting
3460                     // both edges of the current run around our 'index'
3461                     attributeRunStartIndex =
3462                         (attributeRunStartIndex != Integer.MIN_VALUE) ?
3463                         attributeRunStartIndex : getRunEdge(tempIndex, -1);
3464                     attributeRunEndIndex =
3465                         (attributeRunEndIndex != Integer.MIN_VALUE) ?
3466                         attributeRunEndIndex : getRunEdge(tempIndex, 1);
3467 
3468                     runText = model.getText(attributeRunStartIndex,
3469                                             attributeRunEndIndex -
3470                                             attributeRunStartIndex);
3471                 } catch (BadLocationException e) {
3472                     // we are intentionally silent; our contract says we return
3473                     // null if there is any failure in this method
3474                     return null;
3475                 } finally {
3476                     if (model instanceof AbstractDocument) {
3477                         ((AbstractDocument)model).readUnlock();
3478                     }
3479                 }
3480                 return new AccessibleTextSequence(attributeRunStartIndex,
3481                                                   attributeRunEndIndex,
3482                                                   runText);
3483 
3484             default:
3485                 break;
3486             }
3487             return null;
3488         }
3489 
3490 
3491         /**
3492          * Starting at text position <code>index</code>, and going in
3493          * <code>direction</code>, return the edge of run that shares the
3494          * same <code>AttributeSet</code> and parent element as those at
3495          * <code>index</code>.
3496          *
3497          * Note: we assume the document is already locked...
3498          */
3499         private int getRunEdge(int index, int direction) throws
3500          BadLocationException {
3501             if (index < 0 || index >= model.getLength()) {
3502                 throw new BadLocationException("Location out of bounds", index);
3503             }
3504             // locate the Element at index
3505             Element indexElement;
3506             // locate the Element at our index/offset
3507             int elementIndex = -1;        // test for initialization
3508             for (indexElement = model.getDefaultRootElement();
3509                  ! indexElement.isLeaf(); ) {
3510                 elementIndex = indexElement.getElementIndex(index);
3511                 indexElement = indexElement.getElement(elementIndex);
3512             }
3513             if (elementIndex == -1) {
3514                 throw new AssertionError(index);
3515             }
3516             // cache the AttributeSet and parentElement atindex
3517             AttributeSet indexAS = indexElement.getAttributes();
3518             Element parent = indexElement.getParentElement();
3519 
3520             // find the first Element before/after ours w/the same AttributeSet
3521             // if we are already at edge of the first element in our parent
3522             // then return that edge
3523             Element edgeElement;
3524             switch (direction) {
3525             case -1:
3526             case 1:
3527                 int edgeElementIndex = elementIndex;
3528                 int elementCount = parent.getElementCount();
3529                 while ((edgeElementIndex + direction) > 0 &&
3530                        ((edgeElementIndex + direction) < elementCount) &&
3531                        parent.getElement(edgeElementIndex
3532                        + direction).getAttributes().isEqual(indexAS)) {
3533                     edgeElementIndex += direction;
3534                 }
3535                 edgeElement = parent.getElement(edgeElementIndex);
3536                 break;
3537             default:
3538                 throw new AssertionError(direction);
3539             }
3540             switch (direction) {
3541             case -1:
3542                 return edgeElement.getStartOffset();
3543             case 1:
3544                 return edgeElement.getEndOffset();
3545             default:
3546                 // we already caught this case earlier; this is to satisfy
3547                 // the compiler...
3548                 return Integer.MIN_VALUE;
3549             }
3550         }
3551 
3552         // getTextRange() not needed; defined in AccessibleEditableText
3553 
3554         /**
3555          * Returns the <code>AccessibleTextSequence</code> at a given
3556          * <code>index</code>.
3557          *
3558          * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3559          * <code>SENTENCE</code>, <code>LINE</code> or
3560          * <code>ATTRIBUTE_RUN</code> to retrieve
3561          * @param index an index within the text
3562          * @return an <code>AccessibleTextSequence</code> specifying the text if
3563          * <code>part</code> and <code>index</code> are valid.  Otherwise,
3564          * <code>null</code> is returned
3565          *
3566          * @see javax.accessibility.AccessibleText#CHARACTER
3567          * @see javax.accessibility.AccessibleText#WORD
3568          * @see javax.accessibility.AccessibleText#SENTENCE
3569          * @see javax.accessibility.AccessibleExtendedText#LINE
3570          * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3571          *
3572          * @since 1.6
3573          */
3574         public AccessibleTextSequence getTextSequenceAt(int part, int index) {
3575             return getSequenceAtIndex(part, index, 0);
3576         }
3577 
3578         /**
3579          * Returns the <code>AccessibleTextSequence</code> after a given
3580          * <code>index</code>.
3581          *
3582          * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3583          * <code>SENTENCE</code>, <code>LINE</code> or
3584          * <code>ATTRIBUTE_RUN</code> to retrieve
3585          * @param index an index within the text
3586          * @return an <code>AccessibleTextSequence</code> specifying the text
3587          * if <code>part</code> and <code>index</code> are valid.  Otherwise,
3588          * <code>null</code> is returned
3589          *
3590          * @see javax.accessibility.AccessibleText#CHARACTER
3591          * @see javax.accessibility.AccessibleText#WORD
3592          * @see javax.accessibility.AccessibleText#SENTENCE
3593          * @see javax.accessibility.AccessibleExtendedText#LINE
3594          * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3595          *
3596          * @since 1.6
3597          */
3598         public AccessibleTextSequence getTextSequenceAfter(int part, int index) {
3599             return getSequenceAtIndex(part, index, 1);
3600         }
3601 
3602         /**
3603          * Returns the <code>AccessibleTextSequence</code> before a given
3604          * <code>index</code>.
3605          *
3606          * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3607          * <code>SENTENCE</code>, <code>LINE</code> or
3608          * <code>ATTRIBUTE_RUN</code> to retrieve
3609          * @param index an index within the text
3610          * @return an <code>AccessibleTextSequence</code> specifying the text
3611          * if <code>part</code> and <code>index</code> are valid.  Otherwise,
3612          * <code>null</code> is returned
3613          *
3614          * @see javax.accessibility.AccessibleText#CHARACTER
3615          * @see javax.accessibility.AccessibleText#WORD
3616          * @see javax.accessibility.AccessibleText#SENTENCE
3617          * @see javax.accessibility.AccessibleExtendedText#LINE
3618          * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3619          *
3620          * @since 1.6
3621          */
3622         public AccessibleTextSequence getTextSequenceBefore(int part, int index) {
3623             return getSequenceAtIndex(part, index, -1);
3624         }
3625 
3626         /**
3627          * Returns the <code>Rectangle</code> enclosing the text between
3628          * two indicies.
3629          *
3630          * @param startIndex the start index in the text
3631          * @param endIndex the end index in the text
3632          * @return the bounding rectangle of the text if the indices are valid.
3633          * Otherwise, <code>null</code> is returned
3634          *
3635          * @since 1.6
3636          */
3637         public Rectangle getTextBounds(int startIndex, int endIndex) {
3638             if (startIndex < 0 || startIndex > model.getLength()-1 ||
3639                 endIndex < 0 || endIndex > model.getLength()-1 ||
3640                 startIndex > endIndex) {
3641                 return null;
3642             }
3643             TextUI ui = getUI();
3644             if (ui == null) {
3645                 return null;
3646             }
3647             Rectangle rect = null;
3648             Rectangle alloc = getRootEditorRect();
3649             if (alloc == null) {
3650                 return null;
3651             }
3652             if (model instanceof AbstractDocument) {
3653                 ((AbstractDocument)model).readLock();
3654             }
3655             try {
3656                 View rootView = ui.getRootView(JTextComponent.this);
3657                 if (rootView != null) {
3658                     Shape bounds = rootView.modelToView(startIndex,
3659                                     Position.Bias.Forward, endIndex,
3660                                     Position.Bias.Backward, alloc);
3661 
3662                     rect = (bounds instanceof Rectangle) ?
3663                      (Rectangle)bounds : bounds.getBounds();
3664 
3665                 }
3666             } catch (BadLocationException e) {
3667             } finally {
3668                 if (model instanceof AbstractDocument) {
3669                     ((AbstractDocument)model).readUnlock();
3670                 }
3671             }
3672             return rect;
3673         }
3674 
3675         // ----- end AccessibleExtendedText methods
3676 
3677 
3678         // --- interface AccessibleAction methods ------------------------
3679 
3680         public AccessibleAction getAccessibleAction() {
3681             return this;
3682         }
3683 
3684         /**
3685          * Returns the number of accessible actions available in this object
3686          * If there are more than one, the first one is considered the
3687          * "default" action of the object.
3688          *
3689          * @return the zero-based number of Actions in this object
3690          * @since 1.4
3691          */
3692         public int getAccessibleActionCount() {
3693             Action [] actions = JTextComponent.this.getActions();
3694             return actions.length;
3695         }
3696 
3697         /**
3698          * Returns a description of the specified action of the object.
3699          *
3700          * @param i zero-based index of the actions
3701          * @return a String description of the action
3702          * @see #getAccessibleActionCount
3703          * @since 1.4
3704          */
3705         public String getAccessibleActionDescription(int i) {
3706             Action [] actions = JTextComponent.this.getActions();
3707             if (i < 0 || i >= actions.length) {
3708                 return null;
3709             }
3710             return (String)actions[i].getValue(Action.NAME);
3711         }
3712 
3713         /**
3714          * Performs the specified Action on the object
3715          *
3716          * @param i zero-based index of actions
3717          * @return true if the action was performed; otherwise false.
3718          * @see #getAccessibleActionCount
3719          * @since 1.4
3720          */
3721         public boolean doAccessibleAction(int i) {
3722             Action [] actions = JTextComponent.this.getActions();
3723             if (i < 0 || i >= actions.length) {
3724                 return false;
3725             }
3726             ActionEvent ae =
3727                 new ActionEvent(JTextComponent.this,
3728                                 ActionEvent.ACTION_PERFORMED, null,
3729                                 EventQueue.getMostRecentEventTime(),
3730                                 getCurrentEventModifiers());
3731             actions[i].actionPerformed(ae);
3732             return true;
3733         }
3734 
3735         // ----- end AccessibleAction methods
3736 
3737 
3738     }
3739 
3740 
3741     // --- serialization ---------------------------------------------
3742 
3743     private void readObject(ObjectInputStream s)
3744         throws IOException, ClassNotFoundException
3745     {
3746         ObjectInputStream.GetField f = s.readFields();
3747 
3748         model = (Document) f.get("model", null);
3749         navigationFilter = (NavigationFilter) f.get("navigationFilter", null);
3750         caretColor = (Color) f.get("caretColor", null);
3751         selectionColor = (Color) f.get("selectionColor", null);
3752         selectedTextColor = (Color) f.get("selectedTextColor", null);
3753         disabledTextColor = (Color) f.get("disabledTextColor", null);
3754         editable = f.get("editable", false);
3755         margin = (Insets) f.get("margin", null);
3756         focusAccelerator = f.get("focusAccelerator", '\0');
3757         boolean newDragEnabled = f.get("dragEnabled", false);
3758         checkDragEnabled(newDragEnabled);
3759         dragEnabled = newDragEnabled;
3760         DropMode newDropMode = (DropMode) f.get("dropMode",
3761                 DropMode.USE_SELECTION);
3762         checkDropMode(newDropMode);
3763         dropMode = newDropMode;
3764         composedTextAttribute = (SimpleAttributeSet) f.get("composedTextAttribute", null);
3765         composedTextContent = (String) f.get("composedTextContent", null);
3766         composedTextStart = (Position) f.get("composedTextStart", null);
3767         composedTextEnd = (Position) f.get("composedTextEnd", null);
3768         latestCommittedTextStart = (Position) f.get("latestCommittedTextStart", null);
3769         latestCommittedTextEnd = (Position) f.get("latestCommittedTextEnd", null);
3770         composedTextCaret = (ComposedTextCaret) f.get("composedTextCaret", null);
3771         checkedInputOverride = f.get("checkedInputOverride", false);
3772         needToSendKeyTypedEvent = f.get("needToSendKeyTypedEvent", false);
3773 
3774         caretEvent = new MutableCaretEvent(this);
3775         addMouseListener(caretEvent);
3776         addFocusListener(caretEvent);
3777     }
3778 
3779     // --- member variables ----------------------------------
3780 
3781     /**
3782      * The document model.
3783      */
3784     private Document model;
3785 
3786     /**
3787      * The caret used to display the insert position
3788      * and navigate throughout the document.
3789      *
3790      * PENDING(prinz)
3791      * This should be serializable, default installed
3792      * by UI.
3793      */
3794     private transient Caret caret;
3795 
3796     /**
3797      * Object responsible for restricting the cursor navigation.
3798      */
3799     private NavigationFilter navigationFilter;
3800 
3801     /**
3802      * The object responsible for managing highlights.
3803      *
3804      * PENDING(prinz)
3805      * This should be serializable, default installed
3806      * by UI.
3807      */
3808     private transient Highlighter highlighter;
3809 
3810     /**
3811      * The current key bindings in effect.
3812      *
3813      * PENDING(prinz)
3814      * This should be serializable, default installed
3815      * by UI.
3816      */
3817     private transient Keymap keymap;
3818 
3819     private transient MutableCaretEvent caretEvent;
3820     private Color caretColor;
3821     private Color selectionColor;
3822     private Color selectedTextColor;
3823     private Color disabledTextColor;
3824     private boolean editable;
3825     private Insets margin;
3826     private char focusAccelerator;
3827     private boolean dragEnabled;
3828 
3829     /**
3830      * The drop mode for this component.
3831      */
3832     private DropMode dropMode = DropMode.USE_SELECTION;
3833 
3834     /**
3835      * The drop location.
3836      */
3837     private transient DropLocation dropLocation;
3838 
3839     /**
3840      * Represents a drop location for <code>JTextComponent</code>s.
3841      *
3842      * @see #getDropLocation
3843      * @since 1.6
3844      */
3845     public static final class DropLocation extends TransferHandler.DropLocation {
3846         private final int index;
3847         private final Position.Bias bias;
3848 
3849         private DropLocation(Point p, int index, Position.Bias bias) {
3850             super(p);
3851             this.index = index;
3852             this.bias = bias;
3853         }
3854 
3855         /**
3856          * Returns the index where dropped data should be inserted into the
3857          * associated component. This index represents a position between
3858          * characters, as would be interpreted by a caret.
3859          *
3860          * @return the drop index
3861          */
3862         public int getIndex() {
3863             return index;
3864         }
3865 
3866         /**
3867          * Returns the bias for the drop index.
3868          *
3869          * @return the drop bias
3870          */
3871         public Position.Bias getBias() {
3872             return bias;
3873         }
3874 
3875         /**
3876          * Returns a string representation of this drop location.
3877          * This method is intended to be used for debugging purposes,
3878          * and the content and format of the returned string may vary
3879          * between implementations.
3880          *
3881          * @return a string representation of this drop location
3882          */
3883         public String toString() {
3884             return getClass().getName()
3885                    + "[dropPoint=" + getDropPoint() + ","
3886                    + "index=" + index + ","
3887                    + "bias=" + bias + "]";
3888         }
3889     }
3890 
3891     /**
3892      * TransferHandler used if one hasn't been supplied by the UI.
3893      */
3894     private static DefaultTransferHandler defaultTransferHandler;
3895 
3896     /**
3897      * Maps from class name to Boolean indicating if
3898      * <code>processInputMethodEvent</code> has been overriden.
3899      */
3900     private static Cache<Class<?>,Boolean> METHOD_OVERRIDDEN
3901             = new Cache<Class<?>,Boolean>(Cache.Kind.WEAK, Cache.Kind.STRONG) {
3902         /**
3903          * Returns {@code true} if the specified {@code type} extends {@link JTextComponent}
3904          * and the {@link JTextComponent#processInputMethodEvent} method is overridden.
3905          */
3906         @Override
3907         public Boolean create(final Class<?> type) {
3908             if (JTextComponent.class == type) {
3909                 return Boolean.FALSE;
3910             }
3911             if (get(type.getSuperclass())) {
3912                 return Boolean.TRUE;
3913             }
3914             return AccessController.doPrivileged(
3915                     new PrivilegedAction<Boolean>() {
3916                         public Boolean run() {
3917                             try {
3918                                 type.getDeclaredMethod("processInputMethodEvent", InputMethodEvent.class);
3919                                 return Boolean.TRUE;
3920                             } catch (NoSuchMethodException exception) {
3921                                 return Boolean.FALSE;
3922                             }
3923                         }
3924                     });
3925         }
3926     };
3927 
3928     /**
3929      * Returns a string representation of this <code>JTextComponent</code>.
3930      * This method is intended to be used only for debugging purposes, and the
3931      * content and format of the returned string may vary between
3932      * implementations. The returned string may be empty but may not
3933      * be <code>null</code>.
3934      * <P>
3935      * Overriding <code>paramString</code> to provide information about the
3936      * specific new aspects of the JFC components.
3937      *
3938      * @return  a string representation of this <code>JTextComponent</code>
3939      */
3940     protected String paramString() {
3941         String editableString = (editable ?
3942                                  "true" : "false");
3943         String caretColorString = (caretColor != null ?
3944                                    caretColor.toString() : "");
3945         String selectionColorString = (selectionColor != null ?
3946                                        selectionColor.toString() : "");
3947         String selectedTextColorString = (selectedTextColor != null ?
3948                                           selectedTextColor.toString() : "");
3949         String disabledTextColorString = (disabledTextColor != null ?
3950                                           disabledTextColor.toString() : "");
3951         String marginString = (margin != null ?
3952                                margin.toString() : "");
3953 
3954         return super.paramString() +
3955         ",caretColor=" + caretColorString +
3956         ",disabledTextColor=" + disabledTextColorString +
3957         ",editable=" + editableString +
3958         ",margin=" + marginString +
3959         ",selectedTextColor=" + selectedTextColorString +
3960         ",selectionColor=" + selectionColorString;
3961     }
3962 
3963 
3964     /**
3965      * A Simple TransferHandler that exports the data as a String, and
3966      * imports the data from the String clipboard.  This is only used
3967      * if the UI hasn't supplied one, which would only happen if someone
3968      * hasn't subclassed Basic.
3969      */
3970     static class DefaultTransferHandler extends TransferHandler implements
3971                                         UIResource {
3972         public void exportToClipboard(JComponent comp, Clipboard clipboard,
3973                                       int action) throws IllegalStateException {
3974             if (comp instanceof JTextComponent) {
3975                 JTextComponent text = (JTextComponent)comp;
3976                 int p0 = text.getSelectionStart();
3977                 int p1 = text.getSelectionEnd();
3978                 if (p0 != p1) {
3979                     try {
3980                         Document doc = text.getDocument();
3981                         String srcData = doc.getText(p0, p1 - p0);
3982                         StringSelection contents =new StringSelection(srcData);
3983 
3984                         // this may throw an IllegalStateException,
3985                         // but it will be caught and handled in the
3986                         // action that invoked this method
3987                         clipboard.setContents(contents, null);
3988 
3989                         if (action == TransferHandler.MOVE) {
3990                             doc.remove(p0, p1 - p0);
3991                         }
3992                     } catch (BadLocationException ble) {}
3993                 }
3994             }
3995         }
3996         public boolean importData(JComponent comp, Transferable t) {
3997             if (comp instanceof JTextComponent) {
3998                 DataFlavor flavor = getFlavor(t.getTransferDataFlavors());
3999 
4000                 if (flavor != null) {
4001                     InputContext ic = comp.getInputContext();
4002                     if (ic != null) {
4003                         ic.endComposition();
4004                     }
4005                     try {
4006                         String data = (String)t.getTransferData(flavor);
4007 
4008                         ((JTextComponent)comp).replaceSelection(data);
4009                         return true;
4010                     } catch (UnsupportedFlavorException ufe) {
4011                     } catch (IOException ioe) {
4012                     }
4013                 }
4014             }
4015             return false;
4016         }
4017         public boolean canImport(JComponent comp,
4018                                  DataFlavor[] transferFlavors) {
4019             JTextComponent c = (JTextComponent)comp;
4020             if (!(c.isEditable() && c.isEnabled())) {
4021                 return false;
4022             }
4023             return (getFlavor(transferFlavors) != null);
4024         }
4025         public int getSourceActions(JComponent c) {
4026             return NONE;
4027         }
4028         private DataFlavor getFlavor(DataFlavor[] flavors) {
4029             if (flavors != null) {
4030                 for (DataFlavor flavor : flavors) {
4031                     if (flavor.equals(DataFlavor.stringFlavor)) {
4032                         return flavor;
4033                     }
4034                 }
4035             }
4036             return null;
4037         }
4038     }
4039 
4040     /**
4041      * Returns the JTextComponent that most recently had focus. The returned
4042      * value may currently have focus.
4043      */
4044     static final JTextComponent getFocusedComponent() {
4045         return (JTextComponent)AppContext.getAppContext().
4046             get(FOCUSED_COMPONENT);
4047     }
4048 
4049     private int getCurrentEventModifiers() {
4050         int modifiers = 0;
4051         AWTEvent currentEvent = EventQueue.getCurrentEvent();
4052         if (currentEvent instanceof InputEvent) {
4053             modifiers = ((InputEvent)currentEvent).getModifiers();
4054         } else if (currentEvent instanceof ActionEvent) {
4055             modifiers = ((ActionEvent)currentEvent).getModifiers();
4056         }
4057         return modifiers;
4058     }
4059 
4060     private static final Object KEYMAP_TABLE =
4061         new StringBuilder("JTextComponent_KeymapTable");
4062 
4063     //
4064     // member variables used for on-the-spot input method
4065     // editing style support
4066     //
4067     private transient InputMethodRequests inputMethodRequestsHandler;
4068     private SimpleAttributeSet composedTextAttribute;
4069     private String composedTextContent;
4070     private Position composedTextStart;
4071     private Position composedTextEnd;
4072     private Position latestCommittedTextStart;
4073     private Position latestCommittedTextEnd;
4074     private ComposedTextCaret composedTextCaret;
4075     private transient Caret originalCaret;
4076     /**
4077      * Set to true after the check for the override of processInputMethodEvent
4078      * has been checked.
4079      */
4080     private boolean checkedInputOverride;
4081     private boolean needToSendKeyTypedEvent;
4082 
4083     static class DefaultKeymap implements Keymap {
4084 
4085         DefaultKeymap(String nm, Keymap parent) {
4086             this.nm = nm;
4087             this.parent = parent;
4088             bindings = new Hashtable<KeyStroke, Action>();
4089         }
4090 
4091         /**
4092          * Fetch the default action to fire if a
4093          * key is typed (ie a KEY_TYPED KeyEvent is received)
4094          * and there is no binding for it.  Typically this
4095          * would be some action that inserts text so that
4096          * the keymap doesn't require an action for each
4097          * possible key.
4098          */
4099         public Action getDefaultAction() {
4100             if (defaultAction != null) {
4101                 return defaultAction;
4102             }
4103             return (parent != null) ? parent.getDefaultAction() : null;
4104         }
4105 
4106         /**
4107          * Set the default action to fire if a key is typed.
4108          */
4109         public void setDefaultAction(Action a) {
4110             defaultAction = a;
4111         }
4112 
4113         public String getName() {
4114             return nm;
4115         }
4116 
4117         public Action getAction(KeyStroke key) {
4118             Action a = bindings.get(key);
4119             if ((a == null) && (parent != null)) {
4120                 a = parent.getAction(key);
4121             }
4122             return a;
4123         }
4124 
4125         public KeyStroke[] getBoundKeyStrokes() {
4126             KeyStroke[] keys = new KeyStroke[bindings.size()];
4127             int i = 0;
4128             for (Enumeration<KeyStroke> e = bindings.keys() ; e.hasMoreElements() ;) {
4129                 keys[i++] = e.nextElement();
4130             }
4131             return keys;
4132         }
4133 
4134         public Action[] getBoundActions() {
4135             Action[] actions = new Action[bindings.size()];
4136             int i = 0;
4137             for (Enumeration<Action> e = bindings.elements() ; e.hasMoreElements() ;) {
4138                 actions[i++] = e.nextElement();
4139             }
4140             return actions;
4141         }
4142 
4143         public KeyStroke[] getKeyStrokesForAction(Action a) {
4144             if (a == null) {
4145                 return null;
4146             }
4147             KeyStroke[] retValue = null;
4148             // Determine local bindings first.
4149             Vector<KeyStroke> keyStrokes = null;
4150             for (Enumeration<KeyStroke> keys = bindings.keys(); keys.hasMoreElements();) {
4151                 KeyStroke key = keys.nextElement();
4152                 if (bindings.get(key) == a) {
4153                     if (keyStrokes == null) {
4154                         keyStrokes = new Vector<KeyStroke>();
4155                     }
4156                     keyStrokes.addElement(key);
4157                 }
4158             }
4159             // See if the parent has any.
4160             if (parent != null) {
4161                 KeyStroke[] pStrokes = parent.getKeyStrokesForAction(a);
4162                 if (pStrokes != null) {
4163                     // Remove any bindings defined in the parent that
4164                     // are locally defined.
4165                     int rCount = 0;
4166                     for (int counter = pStrokes.length - 1; counter >= 0;
4167                          counter--) {
4168                         if (isLocallyDefined(pStrokes[counter])) {
4169                             pStrokes[counter] = null;
4170                             rCount++;
4171                         }
4172                     }
4173                     if (rCount > 0 && rCount < pStrokes.length) {
4174                         if (keyStrokes == null) {
4175                             keyStrokes = new Vector<KeyStroke>();
4176                         }
4177                         for (int counter = pStrokes.length - 1; counter >= 0;
4178                              counter--) {
4179                             if (pStrokes[counter] != null) {
4180                                 keyStrokes.addElement(pStrokes[counter]);
4181                             }
4182                         }
4183                     }
4184                     else if (rCount == 0) {
4185                         if (keyStrokes == null) {
4186                             retValue = pStrokes;
4187                         }
4188                         else {
4189                             retValue = new KeyStroke[keyStrokes.size() +
4190                                                     pStrokes.length];
4191                             keyStrokes.copyInto(retValue);
4192                             System.arraycopy(pStrokes, 0, retValue,
4193                                         keyStrokes.size(), pStrokes.length);
4194                             keyStrokes = null;
4195                         }
4196                     }
4197                 }
4198             }
4199             if (keyStrokes != null) {
4200                 retValue = new KeyStroke[keyStrokes.size()];
4201                 keyStrokes.copyInto(retValue);
4202             }
4203             return retValue;
4204         }
4205 
4206         public boolean isLocallyDefined(KeyStroke key) {
4207             return bindings.containsKey(key);
4208         }
4209 
4210         public void addActionForKeyStroke(KeyStroke key, Action a) {
4211             bindings.put(key, a);
4212         }
4213 
4214         public void removeKeyStrokeBinding(KeyStroke key) {
4215             bindings.remove(key);
4216         }
4217 
4218         public void removeBindings() {
4219             bindings.clear();
4220         }
4221 
4222         public Keymap getResolveParent() {
4223             return parent;
4224         }
4225 
4226         public void setResolveParent(Keymap parent) {
4227             this.parent = parent;
4228         }
4229 
4230         /**
4231          * String representation of the keymap... potentially
4232          * a very long string.
4233          */
4234         public String toString() {
4235             return "Keymap[" + nm + "]" + bindings;
4236         }
4237 
4238         String nm;
4239         Keymap parent;
4240         Hashtable<KeyStroke, Action> bindings;
4241         Action defaultAction;
4242     }
4243 
4244 
4245     /**
4246      * KeymapWrapper wraps a Keymap inside an InputMap. For KeymapWrapper
4247      * to be useful it must be used with a KeymapActionMap.
4248      * KeymapWrapper for the most part, is an InputMap with two parents.
4249      * The first parent visited is ALWAYS the Keymap, with the second
4250      * parent being the parent inherited from InputMap. If
4251      * <code>keymap.getAction</code> returns null, implying the Keymap
4252      * does not have a binding for the KeyStroke,
4253      * the parent is then visited. If the Keymap has a binding, the
4254      * Action is returned, if not and the KeyStroke represents a
4255      * KeyTyped event and the Keymap has a defaultAction,
4256      * <code>DefaultActionKey</code> is returned.
4257      * <p>KeymapActionMap is then able to transate the object passed in
4258      * to either message the Keymap, or message its default implementation.
4259      */
4260     static class KeymapWrapper extends InputMap {
4261         static final Object DefaultActionKey = new Object();
4262 
4263         private Keymap keymap;
4264 
4265         KeymapWrapper(Keymap keymap) {
4266             this.keymap = keymap;
4267         }
4268 
4269         public KeyStroke[] keys() {
4270             KeyStroke[] sKeys = super.keys();
4271             KeyStroke[] keymapKeys = keymap.getBoundKeyStrokes();
4272             int sCount = (sKeys == null) ? 0 : sKeys.length;
4273             int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
4274             if (sCount == 0) {
4275                 return keymapKeys;
4276             }
4277             if (keymapCount == 0) {
4278                 return sKeys;
4279             }
4280             KeyStroke[] retValue = new KeyStroke[sCount + keymapCount];
4281             // There may be some duplication here...
4282             System.arraycopy(sKeys, 0, retValue, 0, sCount);
4283             System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
4284             return retValue;
4285         }
4286 
4287         public int size() {
4288             // There may be some duplication here...
4289             KeyStroke[] keymapStrokes = keymap.getBoundKeyStrokes();
4290             int keymapCount = (keymapStrokes == null) ? 0:
4291                                keymapStrokes.length;
4292             return super.size() + keymapCount;
4293         }
4294 
4295         public Object get(KeyStroke keyStroke) {
4296             Object retValue = keymap.getAction(keyStroke);
4297             if (retValue == null) {
4298                 retValue = super.get(keyStroke);
4299                 if (retValue == null &&
4300                     keyStroke.getKeyChar() != KeyEvent.CHAR_UNDEFINED &&
4301                     keymap.getDefaultAction() != null) {
4302                     // Implies this is a KeyTyped event, use the default
4303                     // action.
4304                     retValue = DefaultActionKey;
4305                 }
4306             }
4307             return retValue;
4308         }
4309     }
4310 
4311 
4312     /**
4313      * Wraps a Keymap inside an ActionMap. This is used with
4314      * a KeymapWrapper. If <code>get</code> is passed in
4315      * <code>KeymapWrapper.DefaultActionKey</code>, the default action is
4316      * returned, otherwise if the key is an Action, it is returned.
4317      */
4318     static class KeymapActionMap extends ActionMap {
4319         private Keymap keymap;
4320 
4321         KeymapActionMap(Keymap keymap) {
4322             this.keymap = keymap;
4323         }
4324 
4325         public Object[] keys() {
4326             Object[] sKeys = super.keys();
4327             Object[] keymapKeys = keymap.getBoundActions();
4328             int sCount = (sKeys == null) ? 0 : sKeys.length;
4329             int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
4330             boolean hasDefault = (keymap.getDefaultAction() != null);
4331             if (hasDefault) {
4332                 keymapCount++;
4333             }
4334             if (sCount == 0) {
4335                 if (hasDefault) {
4336                     Object[] retValue = new Object[keymapCount];
4337                     if (keymapCount > 1) {
4338                         System.arraycopy(keymapKeys, 0, retValue, 0,
4339                                          keymapCount - 1);
4340                     }
4341                     retValue[keymapCount - 1] = KeymapWrapper.DefaultActionKey;
4342                     return retValue;
4343                 }
4344                 return keymapKeys;
4345             }
4346             if (keymapCount == 0) {
4347                 return sKeys;
4348             }
4349             Object[] retValue = new Object[sCount + keymapCount];
4350             // There may be some duplication here...
4351             System.arraycopy(sKeys, 0, retValue, 0, sCount);
4352             if (hasDefault) {
4353                 if (keymapCount > 1) {
4354                     System.arraycopy(keymapKeys, 0, retValue, sCount,
4355                                      keymapCount - 1);
4356                 }
4357                 retValue[sCount + keymapCount - 1] = KeymapWrapper.
4358                                                  DefaultActionKey;
4359             }
4360             else {
4361                 System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
4362             }
4363             return retValue;
4364         }
4365 
4366         public int size() {
4367             // There may be some duplication here...
4368             Object[] actions = keymap.getBoundActions();
4369             int keymapCount = (actions == null) ? 0 : actions.length;
4370             if (keymap.getDefaultAction() != null) {
4371                 keymapCount++;
4372             }
4373             return super.size() + keymapCount;
4374         }
4375 
4376         public Action get(Object key) {
4377             Action retValue = super.get(key);
4378             if (retValue == null) {
4379                 // Try the Keymap.
4380                 if (key == KeymapWrapper.DefaultActionKey) {
4381                     retValue = keymap.getDefaultAction();
4382                 }
4383                 else if (key instanceof Action) {
4384                     // This is a little iffy, technically an Action is
4385                     // a valid Key. We're assuming the Action came from
4386                     // the InputMap though.
4387                     retValue = (Action)key;
4388                 }
4389             }
4390             return retValue;
4391         }
4392     }
4393 
4394     private static final Object FOCUSED_COMPONENT =
4395         new StringBuilder("JTextComponent_FocusedComponent");
4396 
4397     /**
4398      * The default keymap that will be shared by all
4399      * <code>JTextComponent</code> instances unless they
4400      * have had a different keymap set.
4401      */
4402     public static final String DEFAULT_KEYMAP = "default";
4403 
4404     /**
4405      * Event to use when firing a notification of change to caret
4406      * position.  This is mutable so that the event can be reused
4407      * since caret events can be fairly high in bandwidth.
4408      */
4409     static class MutableCaretEvent extends CaretEvent implements ChangeListener, FocusListener, MouseListener {
4410 
4411         MutableCaretEvent(JTextComponent c) {
4412             super(c);
4413         }
4414 
4415         final void fire() {
4416             JTextComponent c = (JTextComponent) getSource();
4417             if (c != null) {
4418                 Caret caret = c.getCaret();
4419                 dot = caret.getDot();
4420                 mark = caret.getMark();
4421                 c.fireCaretUpdate(this);
4422             }
4423         }
4424 
4425         public final String toString() {
4426             return "dot=" + dot + "," + "mark=" + mark;
4427         }
4428 
4429         // --- CaretEvent methods -----------------------
4430 
4431         public final int getDot() {
4432             return dot;
4433         }
4434 
4435         public final int getMark() {
4436             return mark;
4437         }
4438 
4439         // --- ChangeListener methods -------------------
4440 
4441         public final void stateChanged(ChangeEvent e) {
4442             if (! dragActive) {
4443                 fire();
4444             }
4445         }
4446 
4447         // --- FocusListener methods -----------------------------------
4448         public void focusGained(FocusEvent fe) {
4449             AppContext.getAppContext().put(FOCUSED_COMPONENT,
4450                                            fe.getSource());
4451         }
4452 
4453         public void focusLost(FocusEvent fe) {
4454         }
4455 
4456         // --- MouseListener methods -----------------------------------
4457 
4458         /**
4459          * Requests focus on the associated
4460          * text component, and try to set the cursor position.
4461          *
4462          * @param e the mouse event
4463          * @see MouseListener#mousePressed
4464          */
4465         public final void mousePressed(MouseEvent e) {
4466             dragActive = true;
4467         }
4468 
4469         /**
4470          * Called when the mouse is released.
4471          *
4472          * @param e the mouse event
4473          * @see MouseListener#mouseReleased
4474          */
4475         public final void mouseReleased(MouseEvent e) {
4476             dragActive = false;
4477             fire();
4478         }
4479 
4480         public final void mouseClicked(MouseEvent e) {
4481         }
4482 
4483         public final void mouseEntered(MouseEvent e) {
4484         }
4485 
4486         public final void mouseExited(MouseEvent e) {
4487         }
4488 
4489         private boolean dragActive;
4490         private int dot;
4491         private int mark;
4492     }
4493 
4494     //
4495     // Process any input method events that the component itself
4496     // recognizes. The default on-the-spot handling for input method
4497     // composed(uncommitted) text is done here after all input
4498     // method listeners get called for stealing the events.
4499     //
4500     @SuppressWarnings("fallthrough")
4501     protected void processInputMethodEvent(InputMethodEvent e) {
4502         // let listeners handle the events
4503         super.processInputMethodEvent(e);
4504 
4505         if (!e.isConsumed()) {
4506             if (! isEditable()) {
4507                 return;
4508             } else {
4509                 switch (e.getID()) {
4510                 case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED:
4511                     replaceInputMethodText(e);
4512 
4513                     // fall through
4514 
4515                 case InputMethodEvent.CARET_POSITION_CHANGED:
4516                     setInputMethodCaretPosition(e);
4517                     break;
4518                 }
4519             }
4520 
4521             e.consume();
4522         }
4523     }
4524 
4525     //
4526     // Overrides this method to become an active input method client.
4527     //
4528     @BeanProperty(bound = false)
4529     public InputMethodRequests getInputMethodRequests() {
4530         if (inputMethodRequestsHandler == null) {
4531             inputMethodRequestsHandler = new InputMethodRequestsHandler();
4532             Document doc = getDocument();
4533             if (doc != null) {
4534                 doc.addDocumentListener((DocumentListener)inputMethodRequestsHandler);
4535             }
4536         }
4537 
4538         return inputMethodRequestsHandler;
4539     }
4540 
4541     //
4542     // Overrides this method to watch the listener installed.
4543     //
4544     public void addInputMethodListener(InputMethodListener l) {
4545         super.addInputMethodListener(l);
4546         if (l != null) {
4547             needToSendKeyTypedEvent = false;
4548             checkedInputOverride = true;
4549         }
4550     }
4551 
4552 
4553     //
4554     // Default implementation of the InputMethodRequests interface.
4555     //
4556     class InputMethodRequestsHandler implements InputMethodRequests, DocumentListener {
4557 
4558         // --- InputMethodRequests methods ---
4559 
4560         public AttributedCharacterIterator cancelLatestCommittedText(
4561                                                 Attribute[] attributes) {
4562             Document doc = getDocument();
4563             if ((doc != null) && (latestCommittedTextStart != null)
4564                 && (!latestCommittedTextStart.equals(latestCommittedTextEnd))) {
4565                 try {
4566                     int startIndex = latestCommittedTextStart.getOffset();
4567                     int endIndex = latestCommittedTextEnd.getOffset();
4568                     String latestCommittedText =
4569                         doc.getText(startIndex, endIndex - startIndex);
4570                     doc.remove(startIndex, endIndex - startIndex);
4571                     return new AttributedString(latestCommittedText).getIterator();
4572                 } catch (BadLocationException ble) {}
4573             }
4574             return null;
4575         }
4576 
4577         public AttributedCharacterIterator getCommittedText(int beginIndex,
4578                                         int endIndex, Attribute[] attributes) {
4579             int composedStartIndex = 0;
4580             int composedEndIndex = 0;
4581             if (composedTextExists()) {
4582                 composedStartIndex = composedTextStart.getOffset();
4583                 composedEndIndex = composedTextEnd.getOffset();
4584             }
4585 
4586             String committed;
4587             try {
4588                 if (beginIndex < composedStartIndex) {
4589                     if (endIndex <= composedStartIndex) {
4590                         committed = getText(beginIndex, endIndex - beginIndex);
4591                     } else {
4592                         int firstPartLength = composedStartIndex - beginIndex;
4593                         committed = getText(beginIndex, firstPartLength) +
4594                             getText(composedEndIndex, endIndex - beginIndex - firstPartLength);
4595                     }
4596                 } else {
4597                     committed = getText(beginIndex + (composedEndIndex - composedStartIndex),
4598                                         endIndex - beginIndex);
4599                 }
4600             } catch (BadLocationException ble) {
4601                 throw new IllegalArgumentException("Invalid range");
4602             }
4603             return new AttributedString(committed).getIterator();
4604         }
4605 
4606         public int getCommittedTextLength() {
4607             Document doc = getDocument();
4608             int length = 0;
4609             if (doc != null) {
4610                 length = doc.getLength();
4611                 if (composedTextContent != null) {
4612                     if (composedTextEnd == null
4613                           || composedTextStart == null) {
4614                         /*
4615                          * fix for : 6355666
4616                          * this is the case when this method is invoked
4617                          * from DocumentListener. At this point
4618                          * composedTextEnd and composedTextStart are
4619                          * not defined yet.
4620                          */
4621                         length -= composedTextContent.length();
4622                     } else {
4623                         length -= composedTextEnd.getOffset() -
4624                             composedTextStart.getOffset();
4625                     }
4626                 }
4627             }
4628             return length;
4629         }
4630 
4631         public int getInsertPositionOffset() {
4632             int composedStartIndex = 0;
4633             int composedEndIndex = 0;
4634             if (composedTextExists()) {
4635                 composedStartIndex = composedTextStart.getOffset();
4636                 composedEndIndex = composedTextEnd.getOffset();
4637             }
4638             int caretIndex = getCaretPosition();
4639 
4640             if (caretIndex < composedStartIndex) {
4641                 return caretIndex;
4642             } else if (caretIndex < composedEndIndex) {
4643                 return composedStartIndex;
4644             } else {
4645                 return caretIndex - (composedEndIndex - composedStartIndex);
4646             }
4647         }
4648 
4649         public TextHitInfo getLocationOffset(int x, int y) {
4650             if (composedTextAttribute == null) {
4651                 return null;
4652             } else {
4653                 Point p = getLocationOnScreen();
4654                 p.x = x - p.x;
4655                 p.y = y - p.y;
4656                 int pos = viewToModel(p);
4657                 if ((pos >= composedTextStart.getOffset()) &&
4658                     (pos <= composedTextEnd.getOffset())) {
4659                     return TextHitInfo.leading(pos - composedTextStart.getOffset());
4660                 } else {
4661                     return null;
4662                 }
4663             }
4664         }
4665 
4666         public Rectangle getTextLocation(TextHitInfo offset) {
4667             Rectangle r;
4668 
4669             try {
4670                 r = modelToView(getCaretPosition());
4671                 if (r != null) {
4672                     Point p = getLocationOnScreen();
4673                     r.translate(p.x, p.y);
4674                 }
4675             } catch (BadLocationException ble) {
4676                 r = null;
4677             }
4678 
4679             if (r == null)
4680                 r = new Rectangle();
4681 
4682             return r;
4683         }
4684 
4685         public AttributedCharacterIterator getSelectedText(
4686                                                 Attribute[] attributes) {
4687             String selection = JTextComponent.this.getSelectedText();
4688             if (selection != null) {
4689                 return new AttributedString(selection).getIterator();
4690             } else {
4691                 return null;
4692             }
4693         }
4694 
4695         // --- DocumentListener methods ---
4696 
4697         public void changedUpdate(DocumentEvent e) {
4698             latestCommittedTextStart = latestCommittedTextEnd = null;
4699         }
4700 
4701         public void insertUpdate(DocumentEvent e) {
4702             latestCommittedTextStart = latestCommittedTextEnd = null;
4703         }
4704 
4705         public void removeUpdate(DocumentEvent e) {
4706             latestCommittedTextStart = latestCommittedTextEnd = null;
4707         }
4708     }
4709 
4710     //
4711     // Replaces the current input method (composed) text according to
4712     // the passed input method event. This method also inserts the
4713     // committed text into the document.
4714     //
4715     private void replaceInputMethodText(InputMethodEvent e) {
4716         int commitCount = e.getCommittedCharacterCount();
4717         AttributedCharacterIterator text = e.getText();
4718         int composedTextIndex;
4719 
4720         // old composed text deletion
4721         Document doc = getDocument();
4722         if (composedTextExists()) {
4723             try {
4724                 doc.remove(composedTextStart.getOffset(),
4725                            composedTextEnd.getOffset() -
4726                            composedTextStart.getOffset());
4727             } catch (BadLocationException ble) {}
4728             composedTextStart = composedTextEnd = null;
4729             composedTextAttribute = null;
4730             composedTextContent = null;
4731         }
4732 
4733         if (text != null) {
4734             text.first();
4735             int committedTextStartIndex = 0;
4736             int committedTextEndIndex = 0;
4737 
4738             // committed text insertion
4739             if (commitCount > 0) {
4740                 // Remember latest committed text start index
4741                 committedTextStartIndex = caret.getDot();
4742 
4743                 // Need to generate KeyTyped events for the committed text for components
4744                 // that are not aware they are active input method clients.
4745                 if (shouldSynthensizeKeyEvents()) {
4746                     for (char c = text.current(); commitCount > 0;
4747                          c = text.next(), commitCount--) {
4748                         KeyEvent ke = new KeyEvent(this, KeyEvent.KEY_TYPED,
4749                                                    EventQueue.getMostRecentEventTime(),
4750                                                    0, KeyEvent.VK_UNDEFINED, c);
4751                         processKeyEvent(ke);
4752                     }
4753                 } else {
4754                     StringBuilder strBuf = new StringBuilder();
4755                     for (char c = text.current(); commitCount > 0;
4756                          c = text.next(), commitCount--) {
4757                         strBuf.append(c);
4758                     }
4759 
4760                     // map it to an ActionEvent
4761                     mapCommittedTextToAction(strBuf.toString());
4762                 }
4763 
4764                 // Remember latest committed text end index
4765                 committedTextEndIndex = caret.getDot();
4766             }
4767 
4768             // new composed text insertion
4769             composedTextIndex = text.getIndex();
4770             if (composedTextIndex < text.getEndIndex()) {
4771                 createComposedTextAttribute(composedTextIndex, text);
4772                 try {
4773                     replaceSelection(null);
4774                     doc.insertString(caret.getDot(), composedTextContent,
4775                                         composedTextAttribute);
4776                     composedTextStart = doc.createPosition(caret.getDot() -
4777                                                 composedTextContent.length());
4778                     composedTextEnd = doc.createPosition(caret.getDot());
4779                 } catch (BadLocationException ble) {
4780                     composedTextStart = composedTextEnd = null;
4781                     composedTextAttribute = null;
4782                     composedTextContent = null;
4783                 }
4784             }
4785 
4786             // Save the latest committed text information
4787             if (committedTextStartIndex != committedTextEndIndex) {
4788                 try {
4789                     latestCommittedTextStart = doc.
4790                         createPosition(committedTextStartIndex);
4791                     latestCommittedTextEnd = doc.
4792                         createPosition(committedTextEndIndex);
4793                 } catch (BadLocationException ble) {
4794                     latestCommittedTextStart =
4795                         latestCommittedTextEnd = null;
4796                 }
4797             } else {
4798                 latestCommittedTextStart =
4799                     latestCommittedTextEnd = null;
4800             }
4801         }
4802     }
4803 
4804     private void createComposedTextAttribute(int composedIndex,
4805                                         AttributedCharacterIterator text) {
4806         Document doc = getDocument();
4807         StringBuilder strBuf = new StringBuilder();
4808 
4809         // create attributed string with no attributes
4810         for (char c = text.setIndex(composedIndex);
4811              c != CharacterIterator.DONE; c = text.next()) {
4812             strBuf.append(c);
4813         }
4814 
4815         composedTextContent = strBuf.toString();
4816         composedTextAttribute = new SimpleAttributeSet();
4817         composedTextAttribute.addAttribute(StyleConstants.ComposedTextAttribute,
4818                 new AttributedString(text, composedIndex, text.getEndIndex()));
4819     }
4820 
4821     /**
4822      * Saves composed text around the specified position.
4823      *
4824      * The composed text (if any) around the specified position is saved
4825      * in a backing store and removed from the document.
4826      *
4827      * @param pos  document position to identify the composed text location
4828      * @return  {@code true} if the composed text exists and is saved,
4829      *          {@code false} otherwise
4830      * @see #restoreComposedText
4831      * @since 1.7
4832      */
4833     protected boolean saveComposedText(int pos) {
4834         if (composedTextExists()) {
4835             int start = composedTextStart.getOffset();
4836             int len = composedTextEnd.getOffset() -
4837                 composedTextStart.getOffset();
4838             if (pos >= start && pos <= start + len) {
4839                 try {
4840                     getDocument().remove(start, len);
4841                     return true;
4842                 } catch (BadLocationException ble) {}
4843             }
4844         }
4845         return false;
4846     }
4847 
4848     /**
4849      * Restores composed text previously saved by {@code saveComposedText}.
4850      *
4851      * The saved composed text is inserted back into the document. This method
4852      * should be invoked only if {@code saveComposedText} returns {@code true}.
4853      *
4854      * @see #saveComposedText
4855      * @since 1.7
4856      */
4857     protected void restoreComposedText() {
4858         Document doc = getDocument();
4859         try {
4860             doc.insertString(caret.getDot(),
4861                              composedTextContent,
4862                              composedTextAttribute);
4863             composedTextStart = doc.createPosition(caret.getDot() -
4864                                 composedTextContent.length());
4865             composedTextEnd = doc.createPosition(caret.getDot());
4866         } catch (BadLocationException ble) {}
4867     }
4868 
4869     //
4870     // Map committed text to an ActionEvent. If the committed text length is 1,
4871     // treat it as a KeyStroke, otherwise or there is no KeyStroke defined,
4872     // treat it just as a default action.
4873     //
4874     private void mapCommittedTextToAction(String committedText) {
4875         Keymap binding = getKeymap();
4876         if (binding != null) {
4877             Action a = null;
4878             if (committedText.length() == 1) {
4879                 KeyStroke k = KeyStroke.getKeyStroke(committedText.charAt(0));
4880                 a = binding.getAction(k);
4881             }
4882 
4883             if (a == null) {
4884                 a = binding.getDefaultAction();
4885             }
4886 
4887             if (a != null) {
4888                 ActionEvent ae =
4889                     new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
4890                                     committedText,
4891                                     EventQueue.getMostRecentEventTime(),
4892                                     getCurrentEventModifiers());
4893                 a.actionPerformed(ae);
4894             }
4895         }
4896     }
4897 
4898     //
4899     // Sets the caret position according to the passed input method
4900     // event. Also, sets/resets composed text caret appropriately.
4901     //
4902     private void setInputMethodCaretPosition(InputMethodEvent e) {
4903         int dot;
4904 
4905         if (composedTextExists()) {
4906             dot = composedTextStart.getOffset();
4907             if (!(caret instanceof ComposedTextCaret)) {
4908                 if (composedTextCaret == null) {
4909                     composedTextCaret = new ComposedTextCaret();
4910                 }
4911                 originalCaret = caret;
4912                 // Sets composed text caret
4913                 exchangeCaret(originalCaret, composedTextCaret);
4914             }
4915 
4916             TextHitInfo caretPos = e.getCaret();
4917             if (caretPos != null) {
4918                 int index = caretPos.getInsertionIndex();
4919                 dot += index;
4920                 if (index == 0) {
4921                     // Scroll the component if needed so that the composed text
4922                     // becomes visible.
4923                     try {
4924                         Rectangle d = modelToView(dot);
4925                         Rectangle end = modelToView(composedTextEnd.getOffset());
4926                         Rectangle b = getBounds();
4927                         d.x += Math.min(end.x - d.x, b.width);
4928                         scrollRectToVisible(d);
4929                     } catch (BadLocationException ble) {}
4930                 }
4931             }
4932             caret.setDot(dot);
4933         } else if (caret instanceof ComposedTextCaret) {
4934             dot = caret.getDot();
4935             // Restores original caret
4936             exchangeCaret(caret, originalCaret);
4937             caret.setDot(dot);
4938         }
4939     }
4940 
4941     private void exchangeCaret(Caret oldCaret, Caret newCaret) {
4942         int blinkRate = oldCaret.getBlinkRate();
4943         setCaret(newCaret);
4944         caret.setBlinkRate(blinkRate);
4945         caret.setVisible(hasFocus());
4946     }
4947 
4948     /**
4949      * Returns true if KeyEvents should be synthesized from an InputEvent.
4950      */
4951     private boolean shouldSynthensizeKeyEvents() {
4952         if (!checkedInputOverride) {
4953             // Checks whether the client code overrides processInputMethodEvent.
4954             // If it is overridden, need not to generate KeyTyped events for committed text.
4955             // If it's not, behave as an passive input method client.
4956             needToSendKeyTypedEvent = !METHOD_OVERRIDDEN.get(getClass());
4957             checkedInputOverride = true;
4958         }
4959         return needToSendKeyTypedEvent;
4960     }
4961 
4962     //
4963     // Checks whether a composed text in this text component
4964     //
4965     boolean composedTextExists() {
4966         return (composedTextStart != null);
4967     }
4968 
4969     //
4970     // Caret implementation for editing the composed text.
4971     //
4972     class ComposedTextCaret extends DefaultCaret implements Serializable {
4973         Color bg;
4974 
4975         //
4976         // Get the background color of the component
4977         //
4978         public void install(JTextComponent c) {
4979             super.install(c);
4980 
4981             Document doc = c.getDocument();
4982             if (doc instanceof StyledDocument) {
4983                 StyledDocument sDoc = (StyledDocument)doc;
4984                 Element elem = sDoc.getCharacterElement(c.composedTextStart.getOffset());
4985                 AttributeSet attr = elem.getAttributes();
4986                 bg = sDoc.getBackground(attr);
4987             }
4988 
4989             if (bg == null) {
4990                 bg = c.getBackground();
4991             }
4992         }
4993 
4994         //
4995         // Draw caret in XOR mode.
4996         //
4997         public void paint(Graphics g) {
4998             if(isVisible()) {
4999                 try {
5000                     Rectangle r = component.modelToView(getDot());
5001                     g.setXORMode(bg);
5002                     g.drawLine(r.x, r.y, r.x, r.y + r.height - 1);
5003                     g.setPaintMode();
5004                 } catch (BadLocationException e) {
5005                     // can't render I guess
5006                     //System.err.println("Can't render cursor");
5007                 }
5008             }
5009         }
5010 
5011         //
5012         // If some area other than the composed text is clicked by mouse,
5013         // issue endComposition() to force commit the composed text.
5014         //
5015         protected void positionCaret(MouseEvent me) {
5016             JTextComponent host = component;
5017             Point pt = new Point(me.getX(), me.getY());
5018             int offset = host.viewToModel(pt);
5019             int composedStartIndex = host.composedTextStart.getOffset();
5020             if ((offset < composedStartIndex) ||
5021                 (offset > composedTextEnd.getOffset())) {
5022                 try {
5023                     // Issue endComposition
5024                     Position newPos = host.getDocument().createPosition(offset);
5025                     host.getInputContext().endComposition();
5026 
5027                     // Post a caret positioning runnable to assure that the positioning
5028                     // occurs *after* committing the composed text.
5029                     EventQueue.invokeLater(new DoSetCaretPosition(host, newPos));
5030                 } catch (BadLocationException ble) {
5031                     System.err.println(ble);
5032                 }
5033             } else {
5034                 // Normal processing
5035                 super.positionCaret(me);
5036             }
5037         }
5038     }
5039 
5040     //
5041     // Runnable class for invokeLater() to set caret position later.
5042     //
5043     private class DoSetCaretPosition implements Runnable {
5044         JTextComponent host;
5045         Position newPos;
5046 
5047         DoSetCaretPosition(JTextComponent host, Position newPos) {
5048             this.host = host;
5049             this.newPos = newPos;
5050         }
5051 
5052         public void run() {
5053             host.setCaretPosition(newPos.getOffset());
5054         }
5055     }
5056 }