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