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