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

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