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