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