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