Home » openjdk-7 » javax » swing » text » [javadoc | source]

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

Home » openjdk-7 » javax » swing » text » [javadoc | source]