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