Source Code Cross Referenced for JTextComponent.java in  » 6.0-JDK-Core » swing » javax » swing » text » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025        package javax.swing.text;
0026
0027        import java.lang.reflect.Method;
0028
0029        import java.security.AccessController;
0030        import java.security.PrivilegedAction;
0031
0032        import java.util.Collections;
0033        import java.util.HashMap;
0034        import java.util.Hashtable;
0035        import java.util.Enumeration;
0036        import java.util.Vector;
0037        import java.util.Iterator;
0038        import java.util.Map;
0039        import java.util.Map.Entry;
0040        import java.util.Set;
0041
0042        import java.util.concurrent.*;
0043
0044        import java.io.*;
0045
0046        import java.awt.*;
0047        import java.awt.event.*;
0048        import java.awt.print.*;
0049        import java.awt.datatransfer.*;
0050        import java.awt.im.InputContext;
0051        import java.awt.im.InputMethodRequests;
0052        import java.awt.font.TextHitInfo;
0053        import java.awt.font.TextAttribute;
0054
0055        import java.awt.print.Printable;
0056        import java.awt.print.PrinterException;
0057
0058        import javax.print.PrintService;
0059        import javax.print.attribute.PrintRequestAttributeSet;
0060
0061        import java.text.*;
0062        import java.text.AttributedCharacterIterator.Attribute;
0063
0064        import javax.swing.*;
0065        import javax.swing.event.*;
0066        import javax.swing.plaf.*;
0067
0068        import javax.accessibility.*;
0069
0070        import javax.print.attribute.*;
0071
0072        import sun.awt.AppContext;
0073
0074        import sun.swing.PrintingStatus;
0075        import sun.swing.SwingUtilities2;
0076        import sun.swing.text.TextComponentPrintable;
0077
0078        /**
0079         * <code>JTextComponent</code> is the base class for swing text 
0080         * components.  It tries to be compatible with the
0081         * <code>java.awt.TextComponent</code> class
0082         * where it can reasonably do so.  Also provided are other services
0083         * for additional flexibility (beyond the pluggable UI and bean
0084         * support).
0085         * You can find information on how to use the functionality
0086         * this class provides in
0087         * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/generaltext.html">General Rules for Using Text Components</a>,
0088         * a section in <em>The Java Tutorial.</em>
0089         *
0090         * <p>
0091         * <dl>
0092         * <dt><b><font size=+1>Caret Changes</font></b>
0093         * <dd>
0094         * The caret is a pluggable object in swing text components.
0095         * Notification of changes to the caret position and the selection
0096         * are sent to implementations of the <code>CaretListener</code>
0097         * interface that have been registered with the text component.
0098         * The UI will install a default caret unless a customized caret
0099         * has been set. <br>
0100         * By default the caret tracks all the document changes
0101         * performed on the Event Dispatching Thread and updates it's position
0102         * accordingly if an insertion occurs before or at the caret position
0103         * or a removal occurs before the caret position. <code>DefaultCaret</code>
0104         * tries to make itself visible which may lead to scrolling
0105         * of a text component within <code>JScrollPane</code>. The default caret
0106         * behavior can be changed by the {@link DefaultCaret#setUpdatePolicy} method.
0107         * <br>
0108         * <b>Note</b>: Non-editable text components also have a caret though
0109         * it may not be painted. 
0110         *
0111         * <p>
0112         * <dt><b><font size=+1>Commands</font></b>
0113         * <dd>
0114         * Text components provide a number of commands that can be used
0115         * to manipulate the component.  This is essentially the way that
0116         * the component expresses its capabilities.  These are expressed
0117         * in terms of the swing <code>Action</code> interface,
0118         * using the <code>TextAction</code> implementation.
0119         * The set of commands supported by the text component can be
0120         * found with the {@link #getActions} method.  These actions
0121         * can be bound to key events, fired from buttons, etc.
0122         *
0123         * <p>
0124         * <dt><b><font size=+1>Text Input</font></b>
0125         * <dd>
0126         * The text components support flexible and internationalized text input, using 
0127         * keymaps and the input method framework, while maintaining compatibility with 
0128         * the AWT listener model.
0129         * <p>
0130         * A {@link javax.swing.text.Keymap} lets an application bind key
0131         * strokes to actions. 
0132         * In order to allow keymaps to be shared across multiple text components, they 
0133         * can use actions that extend <code>TextAction</code>.
0134         * <code>TextAction</code> can determine which <code>JTextComponent</code>
0135         * most recently has or had focus and therefore is the subject of 
0136         * the action (In the case that the <code>ActionEvent</code>
0137         * sent to the action doesn't contain the target text component as its source). 
0138         * <p>
0139         * The <a href="../../../../technotes/guides/imf/spec.html">input method framework</a>
0140         * lets text components interact with input methods, separate software
0141         * components that preprocess events to let users enter thousands of
0142         * different characters using keyboards with far fewer keys.
0143         * <code>JTextComponent</code> is an <em>active client</em> of 
0144         * the framework, so it implements the preferred user interface for interacting 
0145         * with input methods. As a consequence, some key events do not reach the text 
0146         * component because they are handled by an input method, and some text input 
0147         * reaches the text component as committed text within an {@link 
0148         * java.awt.event.InputMethodEvent} instead of as a key event.
0149         * The complete text input is the combination of the characters in
0150         * <code>keyTyped</code> key events and committed text in input method events.
0151         * <p>
0152         * The AWT listener model lets applications attach event listeners to
0153         * components in order to bind events to actions. Swing encourages the
0154         * use of keymaps instead of listeners, but maintains compatibility
0155         * with listeners by giving the listeners a chance to steal an event
0156         * by consuming it.
0157         * <p>
0158         * Keyboard event and input method events are handled in the following stages, 
0159         * with each stage capable of consuming the event:
0160         *
0161         * <table border=1 summary="Stages of keyboard and input method event handling">
0162         * <tr>
0163         * <th id="stage"><p align="left">Stage</p></th>
0164         * <th id="ke"><p align="left">KeyEvent</p></th>
0165         * <th id="ime"><p align="left">InputMethodEvent</p></th></tr>
0166         * <tr><td headers="stage">1.   </td>
0167         *     <td headers="ke">input methods </td>
0168         *     <td headers="ime">(generated here)</td></tr>
0169         * <tr><td headers="stage">2.   </td>
0170         *     <td headers="ke">focus manager </td>
0171         *     <td headers="ime"></td>
0172         * </tr>
0173         * <tr>
0174         *     <td headers="stage">3.   </td>
0175         *     <td headers="ke">registered key listeners</td>
0176         *     <td headers="ime">registered input method listeners</tr>
0177         * <tr>
0178         *     <td headers="stage">4.   </td>
0179         *     <td headers="ke"></td>
0180         *     <td headers="ime">input method handling in JTextComponent</tr>
0181         * <tr>
0182         *     <td headers="stage">5.   </td><td headers="ke ime" colspan=2>keymap handling using the current keymap</td></tr>
0183         * <tr><td headers="stage">6.   </td><td headers="ke">keyboard handling in JComponent (e.g. accelerators, component navigation, etc.)</td>
0184         *     <td headers="ime"></td></tr>
0185         * </table>
0186         *
0187         * <p>
0188         * To maintain compatibility with applications that listen to key
0189         * events but are not aware of input method events, the input
0190         * method handling in stage 4 provides a compatibility mode for
0191         * components that do not process input method events. For these
0192         * components, the committed text is converted to keyTyped key events 
0193         * and processed in the key event pipeline starting at stage 3
0194         * instead of in the input method event pipeline.
0195         * <p>
0196         * By default the component will create a keymap (named <b>DEFAULT_KEYMAP</b>) 
0197         * that is shared by all JTextComponent instances as the default keymap.  
0198         * Typically a look-and-feel implementation will install a different keymap
0199         * that resolves to the default keymap for those bindings not found in the 
0200         * different keymap. The minimal bindings include:
0201         * <ul>
0202         * <li>inserting content into the editor for the
0203         *  printable keys.
0204         * <li>removing content with the backspace and del
0205         *  keys.
0206         * <li>caret movement forward and backward
0207         * </ul>
0208         *
0209         * <p>
0210         * <dt><b><font size=+1>Model/View Split</font></b>
0211         * <dd>
0212         * The text components have a model-view split.  A text component pulls 
0213         * together the objects used to represent the model, view, and controller. 
0214         * The text document model may be shared by other views which act as observers 
0215         * of the model (e.g. a document may be shared by multiple components).
0216         *
0217         * <p align=center><img src="doc-files/editor.gif" alt="Diagram showing interaction between Controller, Document, events, and ViewFactory" 
0218         *                  HEIGHT=358 WIDTH=587></p>
0219         *
0220         * <p>
0221         * The model is defined by the {@link Document} interface.
0222         * This is intended to provide a flexible text storage mechanism
0223         * that tracks change during edits and can be extended to more sophisticated
0224         * models.  The model interfaces are meant to capture the capabilities of
0225         * expression given by SGML, a system used to express a wide variety of
0226         * content.
0227         * Each modification to the document causes notification of the
0228         * details of the change to be sent to all observers in the form of a 
0229         * {@link DocumentEvent} which allows the views to stay up to date with the model.
0230         * This event is sent to observers that have implemented the 
0231         * {@link DocumentListener}
0232         * interface and registered interest with the model being observed.
0233         *
0234         * <p>
0235         * <dt><b><font size=+1>Location Information</font></b>
0236         * <dd>
0237         * The capability of determining the location of text in
0238         * the view is provided.  There are two methods, {@link #modelToView}
0239         * and {@link #viewToModel} for determining this information.
0240         *
0241         * <p>
0242         * <dt><b><font size=+1>Undo/Redo support</font></b>
0243         * <dd>
0244         * Support for an edit history mechanism is provided to allow
0245         * undo/redo operations.  The text component does not itself
0246         * provide the history buffer by default, but does provide
0247         * the <code>UndoableEdit</code> records that can be used in conjunction
0248         * with a history buffer to provide the undo/redo support.
0249         * The support is provided by the Document model, which allows
0250         * one to attach UndoableEditListener implementations.
0251         *
0252         * <p>
0253         * <dt><b><font size=+1>Thread Safety</font></b>
0254         * <dd>
0255         * The swing text components provide some support of thread
0256         * safe operations.  Because of the high level of configurability
0257         * of the text components, it is possible to circumvent the
0258         * protection provided.  The protection primarily comes from
0259         * the model, so the documentation of <code>AbstractDocument</code>
0260         * describes the assumptions of the protection provided.
0261         * The methods that are safe to call asynchronously are marked
0262         * with comments.
0263         *
0264         * <p>
0265         * <dt><b><font size=+1>Newlines</font></b>
0266         * <dd>
0267         * For a discussion on how newlines are handled, see
0268         * <a href="DefaultEditorKit.html">DefaultEditorKit</a>.
0269         *
0270         * <p>
0271         * <dt><b><font size=+1>Printing support</font></b>
0272         * <dd> 
0273         * Several {@link #print print} methods are provided for basic
0274         * document printing.  If more advanced printing is needed, use the 
0275         * {@link #getPrintable} method.
0276         * </dl>
0277         *
0278         * <p>
0279         * <strong>Warning:</strong>
0280         * Serialized objects of this class will not be compatible with
0281         * future Swing releases. The current serialization support is
0282         * appropriate for short term storage or RMI between applications running
0283         * the same version of Swing.  As of 1.4, support for long term storage
0284         * of all JavaBeans<sup><font size="-2">TM</font></sup>
0285         * has been added to the <code>java.beans</code> package.
0286         * Please see {@link java.beans.XMLEncoder}.
0287         *
0288         * @beaninfo
0289         *     attribute: isContainer false
0290         * 
0291         * @author  Timothy Prinzing
0292         * @author Igor Kushnirskiy (printing support)
0293         * @version 1.235 05/05/07
0294         * @see Document
0295         * @see DocumentEvent
0296         * @see DocumentListener
0297         * @see Caret
0298         * @see CaretEvent
0299         * @see CaretListener
0300         * @see TextUI
0301         * @see View
0302         * @see ViewFactory
0303         */
0304        public abstract class JTextComponent extends JComponent implements 
0305                Scrollable, Accessible {
0306            /**
0307             * Creates a new <code>JTextComponent</code>.
0308             * Listeners for caret events are established, and the pluggable
0309             * UI installed.  The component is marked as editable.  No layout manager
0310             * is used, because layout is managed by the view subsystem of text.
0311             * The document model is set to <code>null</code>.
0312             */
0313            public JTextComponent() {
0314                super ();
0315                // enable InputMethodEvent for on-the-spot pre-editing
0316                enableEvents(AWTEvent.KEY_EVENT_MASK
0317                        | AWTEvent.INPUT_METHOD_EVENT_MASK);
0318                caretEvent = new MutableCaretEvent(this );
0319                addMouseListener(caretEvent);
0320                addFocusListener(caretEvent);
0321                setEditable(true);
0322                setDragEnabled(false);
0323                setLayout(null); // layout is managed by View hierarchy
0324                updateUI();
0325            }
0326
0327            /**
0328             * Fetches the user-interface factory for this text-oriented editor.
0329             *
0330             * @return the factory
0331             */
0332            public TextUI getUI() {
0333                return (TextUI) ui;
0334            }
0335
0336            /**
0337             * Sets the user-interface factory for this text-oriented editor.
0338             *
0339             * @param ui the factory
0340             */
0341            public void setUI(TextUI ui) {
0342                super .setUI(ui);
0343            }
0344
0345            /**
0346             * Reloads the pluggable UI.  The key used to fetch the
0347             * new interface is <code>getUIClassID()</code>.  The type of
0348             * the UI is <code>TextUI</code>.  <code>invalidate</code>
0349             * is called after setting the UI.
0350             */
0351            public void updateUI() {
0352                setUI((TextUI) UIManager.getUI(this ));
0353                invalidate();
0354            }
0355
0356            /**
0357             * Adds a caret listener for notification of any changes
0358             * to the caret.
0359             *
0360             * @param listener the listener to be added
0361             * @see javax.swing.event.CaretEvent
0362             */
0363            public void addCaretListener(CaretListener listener) {
0364                listenerList.add(CaretListener.class, listener);
0365            }
0366
0367            /**
0368             * Removes a caret listener.
0369             *
0370             * @param listener the listener to be removed
0371             * @see javax.swing.event.CaretEvent
0372             */
0373            public void removeCaretListener(CaretListener listener) {
0374                listenerList.remove(CaretListener.class, listener);
0375            }
0376
0377            /**
0378             * Returns an array of all the caret listeners
0379             * registered on this text component.
0380             *
0381             * @return all of this component's <code>CaretListener</code>s 
0382             *         or an empty
0383             *         array if no caret listeners are currently registered
0384             *
0385             * @see #addCaretListener
0386             * @see #removeCaretListener
0387             *
0388             * @since 1.4
0389             */
0390            public CaretListener[] getCaretListeners() {
0391                return (CaretListener[]) listenerList
0392                        .getListeners(CaretListener.class);
0393            }
0394
0395            /**
0396             * Notifies all listeners that have registered interest for
0397             * notification on this event type.  The event instance 
0398             * is lazily created using the parameters passed into 
0399             * the fire method.  The listener list is processed in a
0400             * last-to-first manner.
0401             *
0402             * @param e the event
0403             * @see EventListenerList
0404             */
0405            protected void fireCaretUpdate(CaretEvent e) {
0406                // Guaranteed to return a non-null array
0407                Object[] listeners = listenerList.getListenerList();
0408                // Process the listeners last to first, notifying
0409                // those that are interested in this event
0410                for (int i = listeners.length - 2; i >= 0; i -= 2) {
0411                    if (listeners[i] == CaretListener.class) {
0412                        ((CaretListener) listeners[i + 1]).caretUpdate(e);
0413                    }
0414                }
0415            }
0416
0417            /**
0418             * Associates the editor with a text document.
0419             * The currently registered factory is used to build a view for
0420             * the document, which gets displayed by the editor after revalidation.
0421             * A PropertyChange event ("document") is propagated to each listener.
0422             *
0423             * @param doc  the document to display/edit
0424             * @see #getDocument
0425             * @beaninfo
0426             *  description: the text document model
0427             *        bound: true
0428             *       expert: true
0429             */
0430            public void setDocument(Document doc) {
0431                Document old = model;
0432
0433                /*
0434                 * aquire a read lock on the old model to prevent notification of
0435                 * mutations while we disconnecting the old model.
0436                 */
0437                try {
0438                    if (old instanceof  AbstractDocument) {
0439                        ((AbstractDocument) old).readLock();
0440                    }
0441                    if (accessibleContext != null) {
0442                        model
0443                                .removeDocumentListener(((AccessibleJTextComponent) accessibleContext));
0444                    }
0445                    if (inputMethodRequestsHandler != null) {
0446                        model
0447                                .removeDocumentListener((DocumentListener) inputMethodRequestsHandler);
0448                    }
0449                    model = doc;
0450
0451                    // Set the document's run direction property to match the
0452                    // component's ComponentOrientation property.
0453                    Boolean runDir = getComponentOrientation().isLeftToRight() ? TextAttribute.RUN_DIRECTION_LTR
0454                            : TextAttribute.RUN_DIRECTION_RTL;
0455                    if (runDir != doc.getProperty(TextAttribute.RUN_DIRECTION)) {
0456                        doc.putProperty(TextAttribute.RUN_DIRECTION, runDir);
0457                    }
0458                    firePropertyChange("document", old, doc);
0459                } finally {
0460                    if (old instanceof  AbstractDocument) {
0461                        ((AbstractDocument) old).readUnlock();
0462                    }
0463                }
0464
0465                revalidate();
0466                repaint();
0467                if (accessibleContext != null) {
0468                    model
0469                            .addDocumentListener(((AccessibleJTextComponent) accessibleContext));
0470                }
0471                if (inputMethodRequestsHandler != null) {
0472                    model
0473                            .addDocumentListener((DocumentListener) inputMethodRequestsHandler);
0474                }
0475            }
0476
0477            /**
0478             * Fetches the model associated with the editor.  This is
0479             * primarily for the UI to get at the minimal amount of
0480             * state required to be a text editor.  Subclasses will
0481             * return the actual type of the model which will typically
0482             * be something that extends Document.
0483             *
0484             * @return the model
0485             */
0486            public Document getDocument() {
0487                return model;
0488            }
0489
0490            // Override of Component.setComponentOrientation
0491            public void setComponentOrientation(ComponentOrientation o) {
0492                // Set the document's run direction property to match the
0493                // ComponentOrientation property.
0494                Document doc = getDocument();
0495                if (doc != null) {
0496                    Boolean runDir = o.isLeftToRight() ? TextAttribute.RUN_DIRECTION_LTR
0497                            : TextAttribute.RUN_DIRECTION_RTL;
0498                    doc.putProperty(TextAttribute.RUN_DIRECTION, runDir);
0499                }
0500                super .setComponentOrientation(o);
0501            }
0502
0503            /**
0504             * Fetches the command list for the editor.  This is
0505             * the list of commands supported by the plugged-in UI
0506             * augmented by the collection of commands that the
0507             * editor itself supports.  These are useful for binding
0508             * to events, such as in a keymap.
0509             *
0510             * @return the command list
0511             */
0512            public Action[] getActions() {
0513                return getUI().getEditorKit(this ).getActions();
0514            }
0515
0516            /**
0517             * Sets margin space between the text component's border
0518             * and its text.  The text component's default <code>Border</code>
0519             * object will use this value to create the proper margin.
0520             * However, if a non-default border is set on the text component, 
0521             * it is that <code>Border</code> object's responsibility to create the
0522             * appropriate margin space (else this property will effectively 
0523             * be ignored).  This causes a redraw of the component.
0524             * A PropertyChange event ("margin") is sent to all listeners.
0525             *
0526             * @param m the space between the border and the text
0527             * @beaninfo
0528             *  description: desired space between the border and text area
0529             *        bound: true
0530             */
0531            public void setMargin(Insets m) {
0532                Insets old = margin;
0533                margin = m;
0534                firePropertyChange("margin", old, m);
0535                invalidate();
0536            }
0537
0538            /**
0539             * Returns the margin between the text component's border and
0540             * its text.  
0541             *
0542             * @return the margin
0543             */
0544            public Insets getMargin() {
0545                return margin;
0546            }
0547
0548            /**
0549             * Sets the <code>NavigationFilter</code>. <code>NavigationFilter</code>
0550             * is used by <code>DefaultCaret</code> and the default cursor movement
0551             * actions as a way to restrict the cursor movement.
0552             *
0553             * @since 1.4
0554             */
0555            public void setNavigationFilter(NavigationFilter filter) {
0556                navigationFilter = filter;
0557            }
0558
0559            /**
0560             * Returns the <code>NavigationFilter</code>. <code>NavigationFilter</code>
0561             * is used by <code>DefaultCaret</code> and the default cursor movement
0562             * actions as a way to restrict the cursor movement. A null return value
0563             * implies the cursor movement and selection should not be restricted.
0564             *
0565             * @since 1.4
0566             * @return the NavigationFilter
0567             */
0568            public NavigationFilter getNavigationFilter() {
0569                return navigationFilter;
0570            }
0571
0572            /**
0573             * Fetches the caret that allows text-oriented navigation over
0574             * the view.  
0575             *
0576             * @return the caret
0577             */
0578            public Caret getCaret() {
0579                return caret;
0580            }
0581
0582            /**
0583             * Sets the caret to be used.  By default this will be set
0584             * by the UI that gets installed.  This can be changed to
0585             * a custom caret if desired.  Setting the caret results in a
0586             * PropertyChange event ("caret") being fired.
0587             *
0588             * @param c the caret
0589             * @see #getCaret
0590             * @beaninfo
0591             *  description: the caret used to select/navigate
0592             *        bound: true
0593             *       expert: true
0594             */
0595            public void setCaret(Caret c) {
0596                if (caret != null) {
0597                    caret.removeChangeListener(caretEvent);
0598                    caret.deinstall(this );
0599                }
0600                Caret old = caret;
0601                caret = c;
0602                if (caret != null) {
0603                    caret.install(this );
0604                    caret.addChangeListener(caretEvent);
0605                }
0606                firePropertyChange("caret", old, caret);
0607            }
0608
0609            /**
0610             * Fetches the object responsible for making highlights.
0611             *
0612             * @return the highlighter
0613             */
0614            public Highlighter getHighlighter() {
0615                return highlighter;
0616            }
0617
0618            /**
0619             * Sets the highlighter to be used.  By default this will be set
0620             * by the UI that gets installed.  This can be changed to
0621             * a custom highlighter if desired.  The highlighter can be set to
0622             * <code>null</code> to disable it.
0623             * A PropertyChange event ("highlighter") is fired
0624             * when a new highlighter is installed.
0625             *
0626             * @param h the highlighter
0627             * @see #getHighlighter
0628             * @beaninfo
0629             *  description: object responsible for background highlights
0630             *        bound: true
0631             *       expert: true
0632             */
0633            public void setHighlighter(Highlighter h) {
0634                if (highlighter != null) {
0635                    highlighter.deinstall(this );
0636                }
0637                Highlighter old = highlighter;
0638                highlighter = h;
0639                if (highlighter != null) {
0640                    highlighter.install(this );
0641                }
0642                firePropertyChange("highlighter", old, h);
0643            }
0644
0645            /**
0646             * Sets the keymap to use for binding events to
0647             * actions.  Setting to <code>null</code> effectively disables
0648             * keyboard input.
0649             * A PropertyChange event ("keymap") is fired when a new keymap
0650             * is installed.
0651             *
0652             * @param map the keymap
0653             * @see #getKeymap
0654             * @beaninfo
0655             *  description: set of key event to action bindings to use
0656             *        bound: true
0657             */
0658            public void setKeymap(Keymap map) {
0659                Keymap old = keymap;
0660                keymap = map;
0661                firePropertyChange("keymap", old, keymap);
0662                updateInputMap(old, map);
0663            }
0664
0665            /**
0666             * Turns on or off automatic drag handling. In order to enable automatic
0667             * drag handling, this property should be set to {@code true}, and the
0668             * component's {@code TransferHandler} needs to be {@code non-null}.
0669             * The default value of the {@code dragEnabled} property is {@code false}.
0670             * <p>
0671             * The job of honoring this property, and recognizing a user drag gesture,
0672             * lies with the look and feel implementation, and in particular, the component's
0673             * {@code TextUI}. When automatic drag handling is enabled, most look and
0674             * feels (including those that subclass {@code BasicLookAndFeel}) begin a
0675             * drag and drop operation whenever the user presses the mouse button over
0676             * a selection and then moves the mouse a few pixels. Setting this property to
0677             * {@code true} can therefore have a subtle effect on how selections behave.
0678             * <p>
0679             * If a look and feel is used that ignores this property, you can still
0680             * begin a drag and drop operation by calling {@code exportAsDrag} on the
0681             * component's {@code TransferHandler}.
0682             *
0683             * @param b whether or not to enable automatic drag handling
0684             * @exception HeadlessException if
0685             *            <code>b</code> is <code>true</code> and
0686             *            <code>GraphicsEnvironment.isHeadless()</code>
0687             *            returns <code>true</code>
0688             * @see java.awt.GraphicsEnvironment#isHeadless
0689             * @see #getDragEnabled
0690             * @see #setTransferHandler
0691             * @see TransferHandler
0692             * @since 1.4
0693             *
0694             * @beaninfo
0695             *  description: determines whether automatic drag handling is enabled
0696             *        bound: false
0697             */
0698            public void setDragEnabled(boolean b) {
0699                if (b && GraphicsEnvironment.isHeadless()) {
0700                    throw new HeadlessException();
0701                }
0702                dragEnabled = b;
0703            }
0704
0705            /**
0706             * Returns whether or not automatic drag handling is enabled.
0707             *
0708             * @return the value of the {@code dragEnabled} property
0709             * @see #setDragEnabled
0710             * @since 1.4
0711             */
0712            public boolean getDragEnabled() {
0713                return dragEnabled;
0714            }
0715
0716            /**
0717             * Sets the drop mode for this component. For backward compatibility,
0718             * the default for this property is <code>DropMode.USE_SELECTION</code>.
0719             * Usage of <code>DropMode.INSERT</code> is recommended, however,
0720             * for an improved user experience. It offers similar behavior of dropping
0721             * between text locations, but does so without affecting the actual text
0722             * selection and caret location.
0723             * <p>
0724             * <code>JTextComponents</code> support the following drop modes:
0725             * <ul>
0726             *    <li><code>DropMode.USE_SELECTION</code></li>
0727             *    <li><code>DropMode.INSERT</code></li>
0728             * </ul>
0729             * <p>
0730             * The drop mode is only meaningful if this component has a
0731             * <code>TransferHandler</code> that accepts drops.
0732             *
0733             * @param dropMode the drop mode to use
0734             * @throws IllegalArgumentException if the drop mode is unsupported
0735             *         or <code>null</code>
0736             * @see #getDropMode
0737             * @see #getDropLocation
0738             * @see #setTransferHandler
0739             * @see javax.swing.TransferHandler
0740             * @since 1.6
0741             */
0742            public final void setDropMode(DropMode dropMode) {
0743                if (dropMode != null) {
0744                    switch (dropMode) {
0745                    case USE_SELECTION:
0746                    case INSERT:
0747                        this .dropMode = dropMode;
0748                        return;
0749                    }
0750                }
0751
0752                throw new IllegalArgumentException(dropMode
0753                        + ": Unsupported drop mode for text");
0754            }
0755
0756            /**
0757             * Returns the drop mode for this component.
0758             *
0759             * @return the drop mode for this component
0760             * @see #setDropMode
0761             * @since 1.6
0762             */
0763            public final DropMode getDropMode() {
0764                return dropMode;
0765            }
0766
0767            /**
0768             * Calculates a drop location in this component, representing where a
0769             * drop at the given point should insert data.
0770             * <p>
0771             * Note: This method is meant to override
0772             * <code>JComponent.dropLocationForPoint()</code>, which is package-private
0773             * in javax.swing. <code>TransferHandler</code> will detect text components
0774             * and call this method instead via reflection. It's name should therefore
0775             * not be changed.
0776             *
0777             * @param p the point to calculate a drop location for
0778             * @return the drop location, or <code>null</code>
0779             */
0780            DropLocation dropLocationForPoint(Point p) {
0781                Position.Bias[] bias = new Position.Bias[1];
0782                int index = getUI().viewToModel(this , p, bias);
0783
0784                // viewToModel currently returns null for some HTML content
0785                // when the point is within the component's top inset
0786                if (bias[0] == null) {
0787                    bias[0] = Position.Bias.Forward;
0788                }
0789
0790                return new DropLocation(p, index, bias[0]);
0791            }
0792
0793            /**
0794             * Called to set or clear the drop location during a DnD operation.
0795             * In some cases, the component may need to use it's internal selection
0796             * temporarily to indicate the drop location. To help facilitate this,
0797             * this method returns and accepts as a parameter a state object.
0798             * This state object can be used to store, and later restore, the selection
0799             * state. Whatever this method returns will be passed back to it in
0800             * future calls, as the state parameter. If it wants the DnD system to
0801             * continue storing the same state, it must pass it back every time.
0802             * Here's how this is used:
0803             * <p>
0804             * Let's say that on the first call to this method the component decides
0805             * to save some state (because it is about to use the selection to show
0806             * a drop index). It can return a state object to the caller encapsulating
0807             * any saved selection state. On a second call, let's say the drop location
0808             * is being changed to something else. The component doesn't need to
0809             * restore anything yet, so it simply passes back the same state object
0810             * to have the DnD system continue storing it. Finally, let's say this
0811             * method is messaged with <code>null</code>. This means DnD
0812             * is finished with this component for now, meaning it should restore
0813             * state. At this point, it can use the state parameter to restore
0814             * said state, and of course return <code>null</code> since there's
0815             * no longer anything to store.
0816             * <p>
0817             * Note: This method is meant to override
0818             * <code>JComponent.setDropLocation()</code>, which is package-private
0819             * in javax.swing. <code>TransferHandler</code> will detect text components
0820             * and call this method instead via reflection. It's name should therefore
0821             * not be changed.
0822             *
0823             * @param location the drop location (as calculated by
0824             *        <code>dropLocationForPoint</code>) or <code>null</code>
0825             *        if there's no longer a valid drop location
0826             * @param state the state object saved earlier for this component,
0827             *        or <code>null</code>
0828             * @param forDrop whether or not the method is being called because an
0829             *        actual drop occurred
0830             * @return any saved state for this component, or <code>null</code> if none
0831             */
0832            Object setDropLocation(TransferHandler.DropLocation location,
0833                    Object state, boolean forDrop) {
0834
0835                Object retVal = null;
0836                DropLocation textLocation = (DropLocation) location;
0837
0838                if (dropMode == DropMode.USE_SELECTION) {
0839                    if (textLocation == null) {
0840                        if (state != null) {
0841                            /*
0842                             * This object represents the state saved earlier.
0843                             *     If the caret is a DefaultCaret it will be
0844                             *     an Object array containing, in order:
0845                             *         - the saved caret mark (Integer)
0846                             *         - the saved caret dot (Integer)
0847                             *         - the saved caret visibility (Boolean)
0848                             *         - the saved mark bias (Position.Bias)
0849                             *         - the saved dot bias (Position.Bias)
0850                             *     If the caret is not a DefaultCaret it will
0851                             *     be similar, but will not contain the dot
0852                             *     or mark bias.
0853                             */
0854                            Object[] vals = (Object[]) state;
0855
0856                            if (!forDrop) {
0857                                if (caret instanceof  DefaultCaret) {
0858                                    ((DefaultCaret) caret).setDot(
0859                                            ((Integer) vals[0]).intValue(),
0860                                            (Position.Bias) vals[3]);
0861                                    ((DefaultCaret) caret).moveDot(
0862                                            ((Integer) vals[1]).intValue(),
0863                                            (Position.Bias) vals[4]);
0864                                } else {
0865                                    caret
0866                                            .setDot(((Integer) vals[0])
0867                                                    .intValue());
0868                                    caret.moveDot(((Integer) vals[1])
0869                                            .intValue());
0870                                }
0871                            }
0872
0873                            caret
0874                                    .setVisible(((Boolean) vals[2])
0875                                            .booleanValue());
0876                        }
0877                    } else {
0878                        if (dropLocation == null) {
0879                            boolean visible;
0880
0881                            if (caret instanceof  DefaultCaret) {
0882                                DefaultCaret dc = (DefaultCaret) caret;
0883                                visible = dc.isActive();
0884                                retVal = new Object[] {
0885                                        Integer.valueOf(dc.getMark()),
0886                                        Integer.valueOf(dc.getDot()),
0887                                        Boolean.valueOf(visible),
0888                                        dc.getMarkBias(), dc.getDotBias() };
0889                            } else {
0890                                visible = caret.isVisible();
0891                                retVal = new Object[] {
0892                                        Integer.valueOf(caret.getMark()),
0893                                        Integer.valueOf(caret.getDot()),
0894                                        Boolean.valueOf(visible) };
0895                            }
0896
0897                            caret.setVisible(true);
0898                        } else {
0899                            retVal = state;
0900                        }
0901
0902                        if (caret instanceof  DefaultCaret) {
0903                            ((DefaultCaret) caret).setDot(textLocation
0904                                    .getIndex(), textLocation.getBias());
0905                        } else {
0906                            caret.setDot(textLocation.getIndex());
0907                        }
0908                    }
0909                } else {
0910                    if (textLocation == null) {
0911                        if (state != null) {
0912                            caret.setVisible(((Boolean) state).booleanValue());
0913                        }
0914                    } else {
0915                        if (dropLocation == null) {
0916                            boolean visible = caret instanceof  DefaultCaret ? ((DefaultCaret) caret)
0917                                    .isActive()
0918                                    : caret.isVisible();
0919                            retVal = Boolean.valueOf(visible);
0920                            caret.setVisible(false);
0921                        } else {
0922                            retVal = state;
0923                        }
0924                    }
0925                }
0926
0927                DropLocation old = dropLocation;
0928                dropLocation = textLocation;
0929                firePropertyChange("dropLocation", old, dropLocation);
0930
0931                return retVal;
0932            }
0933
0934            /**
0935             * Returns the location that this component should visually indicate
0936             * as the drop location during a DnD operation over the component,
0937             * or {@code null} if no location is to currently be shown.
0938             * <p>
0939             * This method is not meant for querying the drop location
0940             * from a {@code TransferHandler}, as the drop location is only
0941             * set after the {@code TransferHandler}'s <code>canImport</code>
0942             * has returned and has allowed for the location to be shown.
0943             * <p>
0944             * When this property changes, a property change event with
0945             * name "dropLocation" is fired by the component.
0946             *
0947             * @return the drop location
0948             * @see #setDropMode
0949             * @see TransferHandler#canImport(TransferHandler.TransferSupport)
0950             * @since 1.6
0951             */
0952            public final DropLocation getDropLocation() {
0953                return dropLocation;
0954            }
0955
0956            /**
0957             * Updates the <code>InputMap</code>s in response to a
0958             * <code>Keymap</code> change.
0959             * @param oldKm  the old <code>Keymap</code>
0960             * @param newKm  the new <code>Keymap</code>
0961             */
0962            void updateInputMap(Keymap oldKm, Keymap newKm) {
0963                // Locate the current KeymapWrapper.
0964                InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
0965                InputMap last = km;
0966                while (km != null && !(km instanceof  KeymapWrapper)) {
0967                    last = km;
0968                    km = km.getParent();
0969                }
0970                if (km != null) {
0971                    // Found it, tweak the InputMap that points to it, as well
0972                    // as anything it points to.
0973                    if (newKm == null) {
0974                        if (last != km) {
0975                            last.setParent(km.getParent());
0976                        } else {
0977                            last.setParent(null);
0978                        }
0979                    } else {
0980                        InputMap newKM = new KeymapWrapper(newKm);
0981                        last.setParent(newKM);
0982                        if (last != km) {
0983                            newKM.setParent(km.getParent());
0984                        }
0985                    }
0986                } else if (newKm != null) {
0987                    km = getInputMap(JComponent.WHEN_FOCUSED);
0988                    if (km != null) {
0989                        // Couldn't find it.
0990                        // Set the parent of WHEN_FOCUSED InputMap to be the new one.
0991                        InputMap newKM = new KeymapWrapper(newKm);
0992                        newKM.setParent(km.getParent());
0993                        km.setParent(newKM);
0994                    }
0995                }
0996
0997                // Do the same thing with the ActionMap
0998                ActionMap am = getActionMap();
0999                ActionMap lastAM = am;
1000                while (am != null && !(am instanceof  KeymapActionMap)) {
1001                    lastAM = am;
1002                    am = am.getParent();
1003                }
1004                if (am != null) {
1005                    // Found it, tweak the Actionap that points to it, as well
1006                    // as anything it points to.
1007                    if (newKm == null) {
1008                        if (lastAM != am) {
1009                            lastAM.setParent(am.getParent());
1010                        } else {
1011                            lastAM.setParent(null);
1012                        }
1013                    } else {
1014                        ActionMap newAM = new KeymapActionMap(newKm);
1015                        lastAM.setParent(newAM);
1016                        if (lastAM != am) {
1017                            newAM.setParent(am.getParent());
1018                        }
1019                    }
1020                } else if (newKm != null) {
1021                    am = getActionMap();
1022                    if (am != null) {
1023                        // Couldn't find it.
1024                        // Set the parent of ActionMap to be the new one.
1025                        ActionMap newAM = new KeymapActionMap(newKm);
1026                        newAM.setParent(am.getParent());
1027                        am.setParent(newAM);
1028                    }
1029                }
1030            }
1031
1032            /**
1033             * Fetches the keymap currently active in this text
1034             * component.
1035             *
1036             * @return the keymap
1037             */
1038            public Keymap getKeymap() {
1039                return keymap;
1040            }
1041
1042            /**
1043             * Adds a new keymap into the keymap hierarchy.  Keymap bindings
1044             * resolve from bottom up so an attribute specified in a child
1045             * will override an attribute specified in the parent.
1046             *
1047             * @param nm   the name of the keymap (must be unique within the
1048             *   collection of named keymaps in the document); the name may 
1049             *   be <code>null</code> if the keymap is unnamed,
1050             *   but the caller is responsible for managing the reference
1051             *   returned as an unnamed keymap can't
1052             *   be fetched by name
1053             * @param parent the parent keymap; this may be <code>null</code> if
1054             *   unspecified bindings need not be resolved in some other keymap
1055             * @return the keymap
1056             */
1057            public static Keymap addKeymap(String nm, Keymap parent) {
1058                Keymap map = new DefaultKeymap(nm, parent);
1059                if (nm != null) {
1060                    // add a named keymap, a class of bindings
1061                    getKeymapTable().put(nm, map);
1062                }
1063                return map;
1064            }
1065
1066            /**
1067             * Removes a named keymap previously added to the document.  Keymaps
1068             * with <code>null</code> names may not be removed in this way.
1069             *
1070             * @param nm  the name of the keymap to remove
1071             * @return the keymap that was removed
1072             */
1073            public static Keymap removeKeymap(String nm) {
1074                return getKeymapTable().remove(nm);
1075            }
1076
1077            /**
1078             * Fetches a named keymap previously added to the document.
1079             * This does not work with <code>null</code>-named keymaps.
1080             *
1081             * @param nm  the name of the keymap
1082             * @return the keymap
1083             */
1084            public static Keymap getKeymap(String nm) {
1085                return getKeymapTable().get(nm);
1086            }
1087
1088            private static HashMap<String, Keymap> getKeymapTable() {
1089                synchronized (KEYMAP_TABLE) {
1090                    AppContext appContext = AppContext.getAppContext();
1091                    HashMap<String, Keymap> keymapTable = (HashMap<String, Keymap>) appContext
1092                            .get(KEYMAP_TABLE);
1093                    if (keymapTable == null) {
1094                        keymapTable = new HashMap<String, Keymap>(17);
1095                        appContext.put(KEYMAP_TABLE, keymapTable);
1096                        //initialize default keymap
1097                        Keymap binding = addKeymap(DEFAULT_KEYMAP, null);
1098                        binding
1099                                .setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction());
1100                    }
1101                    return keymapTable;
1102                }
1103            }
1104
1105            /**
1106             * Binding record for creating key bindings.
1107             * <p>
1108             * <strong>Warning:</strong>
1109             * Serialized objects of this class will not be compatible with
1110             * future Swing releases. The current serialization support is
1111             * appropriate for short term storage or RMI between applications running
1112             * the same version of Swing.  As of 1.4, support for long term storage
1113             * of all JavaBeans<sup><font size="-2">TM</font></sup>
1114             * has been added to the <code>java.beans</code> package.
1115             * Please see {@link java.beans.XMLEncoder}.
1116             */
1117            public static class KeyBinding {
1118
1119                /**
1120                 * The key.
1121                 */
1122                public KeyStroke key;
1123
1124                /**
1125                 * The name of the action for the key.
1126                 */
1127                public String actionName;
1128
1129                /**
1130                 * Creates a new key binding.
1131                 *
1132                 * @param key the key
1133                 * @param actionName the name of the action for the key
1134                 */
1135                public KeyBinding(KeyStroke key, String actionName) {
1136                    this .key = key;
1137                    this .actionName = actionName;
1138                }
1139            }
1140
1141            /**
1142             * <p>
1143             * Loads a keymap with a bunch of 
1144             * bindings.  This can be used to take a static table of
1145             * definitions and load them into some keymap.  The following
1146             * example illustrates an example of binding some keys to
1147             * the cut, copy, and paste actions associated with a 
1148             * JTextComponent.  A code fragment to accomplish
1149             * this might look as follows:
1150             * <pre><code>
1151             *
1152             *   static final JTextComponent.KeyBinding[] defaultBindings = {
1153             *     new JTextComponent.KeyBinding(
1154             *       KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
1155             *       DefaultEditorKit.copyAction),
1156             *     new JTextComponent.KeyBinding(
1157             *       KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
1158             *       DefaultEditorKit.pasteAction),
1159             *     new JTextComponent.KeyBinding(
1160             *       KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
1161             *       DefaultEditorKit.cutAction),
1162             *   };
1163             *
1164             *   JTextComponent c = new JTextPane();
1165             *   Keymap k = c.getKeymap();
1166             *   JTextComponent.loadKeymap(k, defaultBindings, c.getActions());
1167             * 
1168             * </code></pre>
1169             * The sets of bindings and actions may be empty but must be
1170             * non-<code>null</code>.
1171             *
1172             * @param map the keymap
1173             * @param bindings the bindings
1174             * @param actions the set of actions
1175             */
1176            public static void loadKeymap(Keymap map, KeyBinding[] bindings,
1177                    Action[] actions) {
1178                Hashtable h = new Hashtable();
1179                for (int i = 0; i < actions.length; i++) {
1180                    Action a = actions[i];
1181                    String value = (String) a.getValue(Action.NAME);
1182                    h.put((value != null ? value : ""), a);
1183                }
1184                for (int i = 0; i < bindings.length; i++) {
1185                    Action a = (Action) h.get(bindings[i].actionName);
1186                    if (a != null) {
1187                        map.addActionForKeyStroke(bindings[i].key, a);
1188                    }
1189                }
1190            }
1191
1192            /**
1193             * Returns true if <code>klass</code> is NOT a JTextComponent and it or
1194             * one of its superclasses (stoping at JTextComponent) overrides
1195             * <code>processInputMethodEvent</code>. It is assumed this will be
1196             * invoked from within a <code>doPrivileged</code>, and it is also
1197             * assumed <code>klass</code> extends <code>JTextComponent</code>.
1198             */
1199            private static Boolean isProcessInputMethodEventOverridden(
1200                    Class klass) {
1201                if (klass == JTextComponent.class) {
1202                    return Boolean.FALSE;
1203                }
1204                Boolean retValue = (Boolean) overrideMap.get(klass.getName());
1205
1206                if (retValue != null) {
1207                    return retValue;
1208                }
1209                Boolean sOverriden = isProcessInputMethodEventOverridden(klass
1210                        .getSuperclass());
1211
1212                if (sOverriden.booleanValue()) {
1213                    // If our superclass has overriden it, then by definition klass
1214                    // overrides it.
1215                    overrideMap.put(klass.getName(), sOverriden);
1216                    return sOverriden;
1217                }
1218                // klass's superclass didn't override it, check for an override in
1219                // klass.
1220                try {
1221                    Class[] classes = new Class[1];
1222                    classes[0] = InputMethodEvent.class;
1223
1224                    Method m = klass.getDeclaredMethod(
1225                            "processInputMethodEvent", classes);
1226                    retValue = Boolean.TRUE;
1227                } catch (NoSuchMethodException nsme) {
1228                    retValue = Boolean.FALSE;
1229                }
1230                overrideMap.put(klass.getName(), retValue);
1231                return retValue;
1232            }
1233
1234            /**
1235             * Fetches the current color used to render the 
1236             * caret.
1237             *
1238             * @return the color
1239             */
1240            public Color getCaretColor() {
1241                return caretColor;
1242            }
1243
1244            /**
1245             * Sets the current color used to render the caret.
1246             * Setting to <code>null</code> effectively restores the default color.
1247             * Setting the color results in a PropertyChange event ("caretColor")
1248             * being fired.
1249             *
1250             * @param c the color
1251             * @see #getCaretColor
1252             * @beaninfo
1253             *  description: the color used to render the caret
1254             *        bound: true
1255             *    preferred: true
1256             */
1257            public void setCaretColor(Color c) {
1258                Color old = caretColor;
1259                caretColor = c;
1260                firePropertyChange("caretColor", old, caretColor);
1261            }
1262
1263            /**
1264             * Fetches the current color used to render the 
1265             * selection.
1266             *
1267             * @return the color
1268             */
1269            public Color getSelectionColor() {
1270                return selectionColor;
1271            }
1272
1273            /**
1274             * Sets the current color used to render the selection.
1275             * Setting the color to <code>null</code> is the same as setting
1276             * <code>Color.white</code>.  Setting the color results in a
1277             * PropertyChange event ("selectionColor").
1278             *
1279             * @param c the color
1280             * @see #getSelectionColor
1281             * @beaninfo
1282             *  description: color used to render selection background
1283             *        bound: true
1284             *    preferred: true
1285             */
1286            public void setSelectionColor(Color c) {
1287                Color old = selectionColor;
1288                selectionColor = c;
1289                firePropertyChange("selectionColor", old, selectionColor);
1290            }
1291
1292            /**
1293             * Fetches the current color used to render the 
1294             * selected text.
1295             *
1296             * @return the color
1297             */
1298            public Color getSelectedTextColor() {
1299                return selectedTextColor;
1300            }
1301
1302            /**
1303             * Sets the current color used to render the selected text.
1304             * Setting the color to <code>null</code> is the same as 
1305             * <code>Color.black</code>. Setting the color results in a
1306             * PropertyChange event ("selectedTextColor") being fired.
1307             *
1308             * @param c the color
1309             * @see #getSelectedTextColor
1310             * @beaninfo
1311             *  description: color used to render selected text
1312             *        bound: true
1313             *    preferred: true
1314             */
1315            public void setSelectedTextColor(Color c) {
1316                Color old = selectedTextColor;
1317                selectedTextColor = c;
1318                firePropertyChange("selectedTextColor", old, selectedTextColor);
1319            }
1320
1321            /**
1322             * Fetches the current color used to render the 
1323             * disabled text.
1324             *
1325             * @return the color
1326             */
1327            public Color getDisabledTextColor() {
1328                return disabledTextColor;
1329            }
1330
1331            /**
1332             * Sets the current color used to render the
1333             * disabled text.  Setting the color fires off a
1334             * PropertyChange event ("disabledTextColor").
1335             *
1336             * @param c the color
1337             * @see #getDisabledTextColor
1338             * @beaninfo
1339             *  description: color used to render disabled text
1340             *        bound: true
1341             *    preferred: true
1342             */
1343            public void setDisabledTextColor(Color c) {
1344                Color old = disabledTextColor;
1345                disabledTextColor = c;
1346                firePropertyChange("disabledTextColor", old, disabledTextColor);
1347            }
1348
1349            /**
1350             * Replaces the currently selected content with new content
1351             * represented by the given string.  If there is no selection
1352             * this amounts to an insert of the given text.  If there
1353             * is no replacement text this amounts to a removal of the
1354             * current selection.  
1355             * <p>
1356             * This is the method that is used by the default implementation
1357             * of the action for inserting content that gets bound to the
1358             * keymap actions.
1359             * <p>
1360             * This method is thread safe, although most Swing methods
1361             * are not. Please see 
1362             * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
1363             * to Use Threads</A> for more information.     
1364             *
1365             * @param content  the content to replace the selection with
1366             */
1367            public void replaceSelection(String content) {
1368                Document doc = getDocument();
1369                if (doc != null) {
1370                    try {
1371                        boolean composedTextSaved = saveComposedText(caret
1372                                .getDot());
1373                        int p0 = Math.min(caret.getDot(), caret.getMark());
1374                        int p1 = Math.max(caret.getDot(), caret.getMark());
1375                        if (doc instanceof  AbstractDocument) {
1376                            ((AbstractDocument) doc).replace(p0, p1 - p0,
1377                                    content, null);
1378                        } else {
1379                            if (p0 != p1) {
1380                                doc.remove(p0, p1 - p0);
1381                            }
1382                            if (content != null && content.length() > 0) {
1383                                doc.insertString(p0, content, null);
1384                            }
1385                        }
1386                        if (composedTextSaved) {
1387                            restoreComposedText();
1388                        }
1389                    } catch (BadLocationException e) {
1390                        UIManager.getLookAndFeel().provideErrorFeedback(
1391                                JTextComponent.this );
1392                    }
1393                }
1394            }
1395
1396            /**
1397             * Fetches a portion of the text represented by the
1398             * component.  Returns an empty string if length is 0.
1399             *
1400             * @param offs the offset >= 0
1401             * @param len the length >= 0
1402             * @return the text
1403             * @exception BadLocationException if the offset or length are invalid
1404             */
1405            public String getText(int offs, int len)
1406                    throws BadLocationException {
1407                return getDocument().getText(offs, len);
1408            }
1409
1410            /**
1411             * Converts the given location in the model to a place in
1412             * the view coordinate system.
1413             * The component must have a positive size for 
1414             * this translation to be computed (i.e. layout cannot
1415             * be computed until the component has been sized).  The
1416             * component does not have to be visible or painted.
1417             *
1418             * @param pos the position >= 0
1419             * @return the coordinates as a rectangle, with (r.x, r.y) as the location
1420             *   in the coordinate system, or null if the component does
1421             *   not yet have a positive size.
1422             * @exception BadLocationException if the given position does not 
1423             *   represent a valid location in the associated document
1424             * @see TextUI#modelToView
1425             */
1426            public Rectangle modelToView(int pos) throws BadLocationException {
1427                return getUI().modelToView(this , pos);
1428            }
1429
1430            /**
1431             * Converts the given place in the view coordinate system
1432             * to the nearest representative location in the model.
1433             * The component must have a positive size for 
1434             * this translation to be computed (i.e. layout cannot
1435             * be computed until the component has been sized).  The
1436             * component does not have to be visible or painted.
1437             *
1438             * @param pt the location in the view to translate
1439             * @return the offset >= 0 from the start of the document, 
1440             *   or -1 if the component does not yet have a positive
1441             *   size.
1442             * @see TextUI#viewToModel
1443             */
1444            public int viewToModel(Point pt) {
1445                return getUI().viewToModel(this , pt);
1446            }
1447
1448            /**
1449             * Transfers the currently selected range in the associated
1450             * text model to the system clipboard, removing the contents
1451             * from the model.  The current selection is reset.  Does nothing
1452             * for <code>null</code> selections.
1453             *
1454             * @see java.awt.Toolkit#getSystemClipboard
1455             * @see java.awt.datatransfer.Clipboard
1456             */
1457            public void cut() {
1458                if (isEditable() && isEnabled()) {
1459                    invokeAction("cut", TransferHandler.getCutAction());
1460                }
1461            }
1462
1463            /**
1464             * Transfers the currently selected range in the associated
1465             * text model to the system clipboard, leaving the contents
1466             * in the text model.  The current selection remains intact.
1467             * Does nothing for <code>null</code> selections.
1468             *
1469             * @see java.awt.Toolkit#getSystemClipboard
1470             * @see java.awt.datatransfer.Clipboard
1471             */
1472            public void copy() {
1473                invokeAction("copy", TransferHandler.getCopyAction());
1474            }
1475
1476            /**
1477             * Transfers the contents of the system clipboard into the
1478             * associated text model.  If there is a selection in the
1479             * associated view, it is replaced with the contents of the
1480             * clipboard.  If there is no selection, the clipboard contents
1481             * are inserted in front of the current insert position in 
1482             * the associated view.  If the clipboard is empty, does nothing.
1483             *
1484             * @see #replaceSelection
1485             * @see java.awt.Toolkit#getSystemClipboard
1486             * @see java.awt.datatransfer.Clipboard
1487             */
1488            public void paste() {
1489                if (isEditable() && isEnabled()) {
1490                    invokeAction("paste", TransferHandler.getPasteAction());
1491                }
1492            }
1493
1494            /**
1495             * This is a conveniance method that is only useful for
1496             * <code>cut</code>, <code>copy</code> and <code>paste</code>.  If
1497             * an <code>Action</code> with the name <code>name</code> does not
1498             * exist in the <code>ActionMap</code>, this will attemp to install a
1499             * <code>TransferHandler</code> and then use <code>altAction</code>.
1500             */
1501            private void invokeAction(String name, Action altAction) {
1502                ActionMap map = getActionMap();
1503                Action action = null;
1504
1505                if (map != null) {
1506                    action = map.get(name);
1507                }
1508                if (action == null) {
1509                    installDefaultTransferHandlerIfNecessary();
1510                    action = altAction;
1511                }
1512                action.actionPerformed(new ActionEvent(this ,
1513                        ActionEvent.ACTION_PERFORMED, (String) action
1514                                .getValue(Action.NAME), EventQueue
1515                                .getMostRecentEventTime(),
1516                        getCurrentEventModifiers()));
1517            }
1518
1519            /**
1520             * If the current <code>TransferHandler</code> is null, this will
1521             * install a new one.
1522             */
1523            private void installDefaultTransferHandlerIfNecessary() {
1524                if (getTransferHandler() == null) {
1525                    if (defaultTransferHandler == null) {
1526                        defaultTransferHandler = new DefaultTransferHandler();
1527                    }
1528                    setTransferHandler(defaultTransferHandler);
1529                }
1530            }
1531
1532            /**
1533             * Moves the caret to a new position, leaving behind a mark
1534             * defined by the last time <code>setCaretPosition</code> was
1535             * called.  This forms a selection.
1536             * If the document is <code>null</code>, does nothing. The position
1537             * must be between 0 and the length of the component's text or else
1538             * an exception is thrown.
1539             *
1540             * @param pos the position
1541             * @exception    IllegalArgumentException if the value supplied
1542             *               for <code>position</code> is less than zero or greater
1543             *               than the component's text length
1544             * @see #setCaretPosition
1545             */
1546            public void moveCaretPosition(int pos) {
1547                Document doc = getDocument();
1548                if (doc != null) {
1549                    if (pos > doc.getLength() || pos < 0) {
1550                        throw new IllegalArgumentException("bad position: "
1551                                + pos);
1552                    }
1553                    caret.moveDot(pos);
1554                }
1555            }
1556
1557            /**
1558             * The bound property name for the focus accelerator.
1559             */
1560            public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
1561
1562            /**
1563             * Sets the key accelerator that will cause the receiving text
1564             * component to get the focus.  The accelerator will be the 
1565             * key combination of the <em>alt</em> key and the character
1566             * given (converted to upper case).  By default, there is no focus
1567             * accelerator key.  Any previous key accelerator setting will be
1568             * superseded.  A '\0' key setting will be registered, and has the
1569             * effect of turning off the focus accelerator.  When the new key
1570             * is set, a PropertyChange event (FOCUS_ACCELERATOR_KEY) will be fired.
1571             *
1572             * @param aKey the key
1573             * @see #getFocusAccelerator
1574             * @beaninfo
1575             *  description: accelerator character used to grab focus
1576             *        bound: true
1577             */
1578            public void setFocusAccelerator(char aKey) {
1579                aKey = Character.toUpperCase(aKey);
1580                char old = focusAccelerator;
1581                focusAccelerator = aKey;
1582                // Fix for 4341002: value of FOCUS_ACCELERATOR_KEY is wrong.
1583                // So we fire both FOCUS_ACCELERATOR_KEY, for compatibility,
1584                // and the correct event here.
1585                firePropertyChange(FOCUS_ACCELERATOR_KEY, old, focusAccelerator);
1586                firePropertyChange("focusAccelerator", old, focusAccelerator);
1587            }
1588
1589            /**
1590             * Returns the key accelerator that will cause the receiving
1591             * text component to get the focus.  Return '\0' if no focus
1592             * accelerator has been set.
1593             *
1594             * @return the key
1595             */
1596            public char getFocusAccelerator() {
1597                return focusAccelerator;
1598            }
1599
1600            /**
1601             * Initializes from a stream.  This creates a
1602             * model of the type appropriate for the component
1603             * and initializes the model from the stream.
1604             * By default this will load the model as plain
1605             * text.  Previous contents of the model are discarded.
1606             *
1607             * @param in the stream to read from
1608             * @param desc an object describing the stream; this
1609             *   might be a string, a File, a URL, etc.  Some kinds
1610             *   of documents (such as html for example) might be
1611             *   able to make use of this information; if non-<code>null</code>,
1612             *   it is added as a property of the document
1613             * @exception IOException as thrown by the stream being
1614             *  used to initialize
1615             * @see EditorKit#createDefaultDocument
1616             * @see #setDocument
1617             * @see PlainDocument
1618             */
1619            public void read(Reader in, Object desc) throws IOException {
1620                EditorKit kit = getUI().getEditorKit(this );
1621                Document doc = kit.createDefaultDocument();
1622                if (desc != null) {
1623                    doc.putProperty(Document.StreamDescriptionProperty, desc);
1624                }
1625                try {
1626                    kit.read(in, doc, 0);
1627                    setDocument(doc);
1628                } catch (BadLocationException e) {
1629                    throw new IOException(e.getMessage());
1630                }
1631            }
1632
1633            /**
1634             * Stores the contents of the model into the given
1635             * stream.  By default this will store the model as plain
1636             * text.
1637             *
1638             * @param out the output stream
1639             * @exception IOException on any I/O error
1640             */
1641            public void write(Writer out) throws IOException {
1642                Document doc = getDocument();
1643                try {
1644                    getUI().getEditorKit(this ).write(out, doc, 0,
1645                            doc.getLength());
1646                } catch (BadLocationException e) {
1647                    throw new IOException(e.getMessage());
1648                }
1649            }
1650
1651            public void removeNotify() {
1652                super .removeNotify();
1653                if (getFocusedComponent() == this ) {
1654                    AppContext.getAppContext().remove(FOCUSED_COMPONENT);
1655                }
1656            }
1657
1658            // --- java.awt.TextComponent methods ------------------------
1659
1660            /**
1661             * Sets the position of the text insertion caret for the 
1662             * <code>TextComponent</code>.  Note that the caret tracks change,
1663             * so this may move if the underlying text of the component is changed.
1664             * If the document is <code>null</code>, does nothing. The position
1665             * must be between 0 and the length of the component's text or else
1666             * an exception is thrown.
1667             *
1668             * @param position the position
1669             * @exception    IllegalArgumentException if the value supplied
1670             *               for <code>position</code> is less than zero or greater
1671             *               than the component's text length
1672             * @beaninfo
1673             * description: the caret position
1674             */
1675            public void setCaretPosition(int position) {
1676                Document doc = getDocument();
1677                if (doc != null) {
1678                    if (position > doc.getLength() || position < 0) {
1679                        throw new IllegalArgumentException("bad position: "
1680                                + 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            public int getCaretPosition() {
1694                return caret.getDot();
1695            }
1696
1697            /**
1698             * Sets the text of this <code>TextComponent</code>
1699             * to the specified text.  If the text is <code>null</code>
1700             * or empty, has the effect of simply deleting the old text.
1701             * When text has been inserted, the resulting caret location
1702             * is determined by the implementation of the caret class.
1703             * <p>
1704             * This method is thread safe, although most Swing methods
1705             * are not. Please see 
1706             * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
1707             * to Use Threads</A> for more information.     
1708             *
1709             * Note that text is not a bound property, so no <code>PropertyChangeEvent
1710             * </code> is fired when it changes. To listen for changes to the text,
1711             * use <code>DocumentListener</code>.
1712             *
1713             * @param t the new text to be set
1714             * @see #getText
1715             * @see DefaultCaret
1716             * @beaninfo
1717             * description: the text of this component
1718             */
1719            public void setText(String t) {
1720                try {
1721                    Document doc = getDocument();
1722                    if (doc instanceof  AbstractDocument) {
1723                        ((AbstractDocument) doc).replace(0, doc.getLength(), t,
1724                                null);
1725                    } else {
1726                        doc.remove(0, doc.getLength());
1727                        doc.insertString(0, t, null);
1728                    }
1729                } catch (BadLocationException e) {
1730                    UIManager.getLookAndFeel().provideErrorFeedback(
1731                            JTextComponent.this );
1732                }
1733            }
1734
1735            /**
1736             * Returns the text contained in this <code>TextComponent</code>.
1737             * If the underlying document is <code>null</code>,
1738             * will give a <code>NullPointerException</code>.
1739             *
1740             * Note that text is not a bound property, so no <code>PropertyChangeEvent
1741             * </code> is fired when it changes. To listen for changes to the text,
1742             * use <code>DocumentListener</code>.
1743             *
1744             * @return the text
1745             * @exception NullPointerException if the document is <code>null</code>
1746             * @see #setText
1747             */
1748            public String getText() {
1749                Document doc = getDocument();
1750                String txt;
1751                try {
1752                    txt = doc.getText(0, doc.getLength());
1753                } catch (BadLocationException e) {
1754                    txt = null;
1755                }
1756                return txt;
1757            }
1758
1759            /**
1760             * Returns the selected text contained in this
1761             * <code>TextComponent</code>.  If the selection is 
1762             * <code>null</code> or the document empty, returns <code>null</code>.
1763             *
1764             * @return the text
1765             * @exception IllegalArgumentException if the selection doesn't
1766             *  have a valid mapping into the document for some reason
1767             * @see #setText
1768             */
1769            public String getSelectedText() {
1770                String txt = null;
1771                int p0 = Math.min(caret.getDot(), caret.getMark());
1772                int p1 = Math.max(caret.getDot(), caret.getMark());
1773                if (p0 != p1) {
1774                    try {
1775                        Document doc = getDocument();
1776                        txt = doc.getText(p0, p1 - p0);
1777                    } catch (BadLocationException e) {
1778                        throw new IllegalArgumentException(e.getMessage());
1779                    }
1780                }
1781                return txt;
1782            }
1783
1784            /**
1785             * Returns the boolean indicating whether this 
1786             * <code>TextComponent</code> is editable or not.
1787             *
1788             * @return the boolean value
1789             * @see #setEditable
1790             */
1791            public boolean isEditable() {
1792                return editable;
1793            }
1794
1795            /**
1796             * Sets the specified boolean to indicate whether or not this
1797             * <code>TextComponent</code> should be editable.
1798             * A PropertyChange event ("editable") is fired when the
1799             * state is changed.
1800             *
1801             * @param b the boolean to be set
1802             * @see #isEditable
1803             * @beaninfo
1804             * description: specifies if the text can be edited
1805             *       bound: true
1806             */
1807            public void setEditable(boolean b) {
1808                if (b != editable) {
1809                    boolean oldVal = editable;
1810                    editable = b;
1811                    enableInputMethods(editable);
1812                    firePropertyChange("editable", Boolean.valueOf(oldVal),
1813                            Boolean.valueOf(editable));
1814                    repaint();
1815                }
1816            }
1817
1818            /**
1819             * Returns the selected text's start position.  Return 0 for an
1820             * empty document, or the value of dot if no selection.
1821             *
1822             * @return the start position >= 0
1823             */
1824            public int getSelectionStart() {
1825                int start = Math.min(caret.getDot(), caret.getMark());
1826                return start;
1827            }
1828
1829            /**
1830             * Sets the selection start to the specified position.  The new
1831             * starting point is constrained to be before or at the current
1832             * selection end.
1833             * <p>
1834             * This is available for backward compatibility to code 
1835             * that called this method on <code>java.awt.TextComponent</code>.
1836             * This is implemented to forward to the <code>Caret</code>
1837             * implementation which is where the actual selection is maintained.
1838             *
1839             * @param selectionStart the start position of the text >= 0
1840             * @beaninfo
1841             * description: starting location of the selection.
1842             */
1843            public void setSelectionStart(int selectionStart) {
1844                /* Route through select method to enforce consistent policy
1845                 * between selectionStart and selectionEnd.
1846                 */
1847                select(selectionStart, getSelectionEnd());
1848            }
1849
1850            /**
1851             * Returns the selected text's end position.  Return 0 if the document
1852             * is empty, or the value of dot if there is no selection.
1853             *
1854             * @return the end position >= 0
1855             */
1856            public int getSelectionEnd() {
1857                int end = Math.max(caret.getDot(), caret.getMark());
1858                return end;
1859            }
1860
1861            /**
1862             * Sets the selection end to the specified position.  The new
1863             * end point is constrained to be at or after the current
1864             * selection start.
1865             * <p>
1866             * This is available for backward compatibility to code 
1867             * that called this method on <code>java.awt.TextComponent</code>.
1868             * This is implemented to forward to the <code>Caret</code>
1869             * implementation which is where the actual selection is maintained.
1870             *
1871             * @param selectionEnd the end position of the text >= 0
1872             * @beaninfo
1873             * description: ending location of the selection.
1874             */
1875            public void setSelectionEnd(int selectionEnd) {
1876                /* Route through select method to enforce consistent policy
1877                 * between selectionStart and selectionEnd.
1878                 */
1879                select(getSelectionStart(), selectionEnd);
1880            }
1881
1882            /**
1883             * Selects the text between the specified start and end positions.
1884             * <p>
1885             * This method sets the start and end positions of the 
1886             * selected text, enforcing the restriction that the start position 
1887             * must be greater than or equal to zero.  The end position must be 
1888             * greater than or equal to the start position, and less than or 
1889             * equal to the length of the text component's text.
1890             * <p>
1891             * If the caller supplies values that are inconsistent or out of 
1892             * bounds, the method enforces these constraints silently, and 
1893             * without failure. Specifically, if the start position or end 
1894             * position is greater than the length of the text, it is reset to 
1895             * equal the text length. If the start position is less than zero, 
1896             * it is reset to zero, and if the end position is less than the 
1897             * start position, it is reset to the start position.
1898             * <p>
1899             * This call is provided for backward compatibility.
1900             * It is routed to a call to <code>setCaretPosition</code>
1901             * followed by a call to <code>moveCaretPosition</code>.
1902             * The preferred way to manage selection is by calling
1903             * those methods directly.
1904             *
1905             * @param selectionStart the start position of the text
1906             * @param selectionEnd the end position of the text
1907             * @see #setCaretPosition
1908             * @see #moveCaretPosition
1909             */
1910            public void select(int selectionStart, int selectionEnd) {
1911                // argument adjustment done by java.awt.TextComponent
1912                int docLength = getDocument().getLength();
1913
1914                if (selectionStart < 0) {
1915                    selectionStart = 0;
1916                }
1917                if (selectionStart > docLength) {
1918                    selectionStart = docLength;
1919                }
1920                if (selectionEnd > docLength) {
1921                    selectionEnd = docLength;
1922                }
1923                if (selectionEnd < selectionStart) {
1924                    selectionEnd = selectionStart;
1925                }
1926
1927                setCaretPosition(selectionStart);
1928                moveCaretPosition(selectionEnd);
1929            }
1930
1931            /**
1932             * Selects all the text in the <code>TextComponent</code>.
1933             * Does nothing on a <code>null</code> or empty document.
1934             */
1935            public void selectAll() {
1936                Document doc = getDocument();
1937                if (doc != null) {
1938                    setCaretPosition(0);
1939                    moveCaretPosition(doc.getLength());
1940                }
1941            }
1942
1943            // --- Tooltip Methods ---------------------------------------------
1944
1945            /**
1946             * Returns the string to be used as the tooltip for <code>event</code>.
1947             * This will return one of:
1948             * <ol>
1949             *  <li>If <code>setToolTipText</code> has been invoked with a 
1950             *      non-<code>null</code>
1951             *      value, it will be returned, otherwise
1952             *  <li>The value from invoking <code>getToolTipText</code> on
1953             *      the UI will be returned.
1954             * </ol>
1955             * By default <code>JTextComponent</code> does not register
1956             * itself with the <code>ToolTipManager</code>.
1957             * This means that tooltips will NOT be shown from the
1958             * <code>TextUI</code> unless <code>registerComponent</code> has
1959             * been invoked on the <code>ToolTipManager</code>.
1960             *
1961             * @param event the event in question
1962             * @return the string to be used as the tooltip for <code>event</code>
1963             * @see javax.swing.JComponent#setToolTipText
1964             * @see javax.swing.plaf.TextUI#getToolTipText
1965             * @see javax.swing.ToolTipManager#registerComponent
1966             */
1967            public String getToolTipText(MouseEvent event) {
1968                String retValue = super .getToolTipText(event);
1969
1970                if (retValue == null) {
1971                    TextUI ui = getUI();
1972                    if (ui != null) {
1973                        retValue = ui.getToolTipText(this , new Point(event
1974                                .getX(), event.getY()));
1975                    }
1976                }
1977                return retValue;
1978            }
1979
1980            // --- Scrollable methods ---------------------------------------------
1981
1982            /**
1983             * Returns the preferred size of the viewport for a view component.
1984             * This is implemented to do the default behavior of returning
1985             * the preferred size of the component.
1986             * 
1987             * @return the <code>preferredSize</code> of a <code>JViewport</code>
1988             * whose view is this <code>Scrollable</code>
1989             */
1990            public Dimension getPreferredScrollableViewportSize() {
1991                return getPreferredSize();
1992            }
1993
1994            /**
1995             * Components that display logical rows or columns should compute
1996             * the scroll increment that will completely expose one new row
1997             * or column, depending on the value of orientation.  Ideally, 
1998             * components should handle a partially exposed row or column by 
1999             * returning the distance required to completely expose the item.
2000             * <p>
2001             * The default implementation of this is to simply return 10% of
2002             * the visible area.  Subclasses are likely to be able to provide
2003             * a much more reasonable value.
2004             * 
2005             * @param visibleRect the view area visible within the viewport
2006             * @param orientation either <code>SwingConstants.VERTICAL</code> or
2007             *   <code>SwingConstants.HORIZONTAL</code>
2008             * @param direction less than zero to scroll up/left, greater than
2009             *   zero for down/right
2010             * @return the "unit" increment for scrolling in the specified direction
2011             * @exception IllegalArgumentException for an invalid orientation
2012             * @see JScrollBar#setUnitIncrement
2013             */
2014            public int getScrollableUnitIncrement(Rectangle visibleRect,
2015                    int orientation, int direction) {
2016                switch (orientation) {
2017                case SwingConstants.VERTICAL:
2018                    return visibleRect.height / 10;
2019                case SwingConstants.HORIZONTAL:
2020                    return visibleRect.width / 10;
2021                default:
2022                    throw new IllegalArgumentException("Invalid orientation: "
2023                            + orientation);
2024                }
2025            }
2026
2027            /**
2028             * Components that display logical rows or columns should compute
2029             * the scroll increment that will completely expose one block
2030             * of rows or columns, depending on the value of orientation. 
2031             * <p>
2032             * The default implementation of this is to simply return the visible
2033             * area.  Subclasses will likely be able to provide a much more 
2034             * reasonable value.
2035             * 
2036             * @param visibleRect the view area visible within the viewport
2037             * @param orientation either <code>SwingConstants.VERTICAL</code> or
2038             *   <code>SwingConstants.HORIZONTAL</code>
2039             * @param direction less than zero to scroll up/left, greater than zero
2040             *  for down/right
2041             * @return the "block" increment for scrolling in the specified direction
2042             * @exception IllegalArgumentException for an invalid orientation
2043             * @see JScrollBar#setBlockIncrement
2044             */
2045            public int getScrollableBlockIncrement(Rectangle visibleRect,
2046                    int orientation, int direction) {
2047                switch (orientation) {
2048                case SwingConstants.VERTICAL:
2049                    return visibleRect.height;
2050                case SwingConstants.HORIZONTAL:
2051                    return visibleRect.width;
2052                default:
2053                    throw new IllegalArgumentException("Invalid orientation: "
2054                            + orientation);
2055                }
2056            }
2057
2058            /**
2059             * Returns true if a viewport should always force the width of this 
2060             * <code>Scrollable</code> to match the width of the viewport.
2061             * For example a normal text view that supported line wrapping
2062             * would return true here, since it would be undesirable for
2063             * wrapped lines to disappear beyond the right
2064             * edge of the viewport.  Note that returning true for a
2065             * <code>Scrollable</code> whose ancestor is a <code>JScrollPane</code>
2066             * effectively disables horizontal scrolling.
2067             * <p>
2068             * Scrolling containers, like <code>JViewport</code>,
2069             * will use this method each time they are validated.  
2070             * 
2071             * @return true if a viewport should force the <code>Scrollable</code>s
2072             *   width to match its own
2073             */
2074            public boolean getScrollableTracksViewportWidth() {
2075                if (getParent() instanceof  JViewport) {
2076                    return (((JViewport) getParent()).getWidth() > getPreferredSize().width);
2077                }
2078                return false;
2079            }
2080
2081            /**
2082             * Returns true if a viewport should always force the height of this 
2083             * <code>Scrollable</code> to match the height of the viewport.
2084             * For example a columnar text view that flowed text in left to
2085             * right columns could effectively disable vertical scrolling by 
2086             * returning true here.
2087             * <p>
2088             * Scrolling containers, like <code>JViewport</code>,
2089             * will use this method each time they are validated.  
2090             * 
2091             * @return true if a viewport should force the Scrollables height
2092             *   to match its own
2093             */
2094            public boolean getScrollableTracksViewportHeight() {
2095                if (getParent() instanceof  JViewport) {
2096                    return (((JViewport) getParent()).getHeight() > getPreferredSize().height);
2097                }
2098                return false;
2099            }
2100
2101            //////////////////
2102            // Printing Support
2103            //////////////////
2104
2105            /**
2106             * A convenience print method that displays a print dialog, and then
2107             * prints this {@code JTextComponent} in <i>interactive</i> mode with no
2108             * header or footer text. Note: this method
2109             * blocks until printing is done.
2110             * <p>
2111             * Note: In <i>headless</i> mode, no dialogs will be shown.
2112             * 
2113             * <p> This method calls the full featured 
2114             * {@link #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2115             * print} method to perform printing.
2116             * @return {@code true}, unless printing is canceled by the user
2117             * @throws PrinterException if an error in the print system causes the job
2118             *         to be aborted
2119             * @throws SecurityException if this thread is not allowed to
2120             *                           initiate a print job request
2121             *         
2122             * @see #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2123             * 
2124             * @since 1.6
2125             */
2126
2127            public boolean print() throws PrinterException {
2128                return print(null, null, true, null, null, true);
2129            }
2130
2131            /**
2132             * A convenience print method that displays a print dialog, and then
2133             * prints this {@code JTextComponent} in <i>interactive</i> mode with 
2134             * the specified header and footer text. Note: this method
2135             * blocks until printing is done.
2136             * <p>
2137             * Note: In <i>headless</i> mode, no dialogs will be shown.
2138             * 
2139             * <p> This method calls the full featured 
2140             * {@link #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2141             * print} method to perform printing.
2142             * @param headerFormat the text, in {@code MessageFormat}, to be
2143             *        used as the header, or {@code null} for no header
2144             * @param footerFormat the text, in {@code MessageFormat}, to be
2145             *        used as the footer, or {@code null} for no footer
2146             * @return {@code true}, unless printing is canceled by the user
2147             * @throws PrinterException if an error in the print system causes the job
2148             *         to be aborted
2149             * @throws SecurityException if this thread is not allowed to
2150             *                           initiate a print job request
2151             *         
2152             * @see #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2153             * @see java.text.MessageFormat     
2154             * @since 1.6
2155             */
2156            public boolean print(final MessageFormat headerFormat,
2157                    final MessageFormat footerFormat) throws PrinterException {
2158                return print(headerFormat, footerFormat, true, null, null, true);
2159            }
2160
2161            /**
2162             * Prints the content of this {@code JTextComponent}. Note: this method
2163             * blocks until printing is done.
2164             * 
2165             * <p>
2166             * Page header and footer text can be added to the output by providing
2167             * {@code MessageFormat} arguments. The printing code requests
2168             * {@code Strings} from the formats, providing a single item which may be
2169             * included in the formatted string: an {@code Integer} representing the
2170             * current page number.
2171             * 
2172             * <p>
2173             * {@code showPrintDialog boolean} parameter allows you to specify whether
2174             * a print dialog is displayed to the user. When it is, the user
2175             * may use the dialog to change printing attributes or even cancel the
2176             * print. 
2177             *
2178             * <p> 
2179             * {@code service} allows you to provide the initial 
2180             * {@code PrintService} for the print dialog, or to specify 
2181             * {@code PrintService} to print to when the dialog is not shown.
2182             *
2183             * <p> 
2184             * {@code attributes} can be used to provide the
2185             * initial values for the print dialog, or to supply any needed
2186             * attributes when the dialog is not shown. {@code attributes} can
2187             * be used to control how the job will print, for example
2188             * <i>duplex</i> or <i>single-sided</i>.
2189             *
2190             * <p> 
2191             * {@code interactive boolean} parameter allows you to specify
2192             * whether to perform printing in <i>interactive</i>
2193             * mode. If {@code true}, a progress dialog, with an abort option,
2194             * is displayed for the duration of printing.  This dialog is
2195             * <i>modal</i> when {@code print} is invoked on the <i>Event Dispatch
2196             * Thread</i> and <i>non-modal</i> otherwise. <b>Warning</b>:
2197             * calling this method on the <i>Event Dispatch Thread</i> with {@code
2198             * interactive false} blocks <i>all</i> events, including repaints, from
2199             * being processed until printing is complete. It is only
2200             * recommended when printing from an application with no
2201             * visible GUI.
2202             *
2203             * <p> 
2204             * Note: In <i>headless</i> mode, {@code showPrintDialog} and
2205             * {@code interactive} parameters are ignored and no dialogs are
2206             * shown.
2207             *
2208             * <p>
2209             * This method ensures the {@code document} is not mutated during printing.
2210             * To indicate it visually, {@code setEnabled(false)} is set for the
2211             * duration of printing.
2212             * 
2213             * <p>
2214             * This method uses {@link #getPrintable} to render document content.
2215             * 
2216             * <p>
2217             * This method is thread-safe, although most Swing methods are not. Please
2218             * see <A
2219             * HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">
2220             * How to Use Threads</A> for more information.
2221             * 
2222             * <p>
2223             * <b>Sample Usage</b>. This code snippet shows a cross-platform print
2224             * dialog and then prints the {@code JTextComponent} in <i>interactive</i> mode
2225             * unless the user cancels the dialog:
2226             * 
2227             * <pre>
2228             * textComponent.print(new MessageFormat(&quot;My text component header&quot;),
2229             *     new MessageFormat(&quot;Footer. Page - {0}&quot;), true, null, null, true);
2230             * </pre>
2231             * <p>
2232             * Executing this code off the <i>Event Dispatch Thread</i> 
2233             * performs printing on the <i>background</i>. 
2234             * The following pattern might be used for <i>background</i> 
2235             * printing:
2236             * <pre>
2237             *     FutureTask&lt;Boolean&gt; future =
2238             *         new FutureTask&lt;Boolean&gt;(
2239             *             new Callable&lt;Boolean&gt;() {
2240             *                 public Boolean call() {
2241             *                     return textComponent.print(.....);
2242             *                 }  
2243             *             });
2244             *     executor.execute(future);
2245             * </pre> 
2246             * 
2247             * @param headerFormat the text, in {@code MessageFormat}, to be
2248             *        used as the header, or {@code null} for no header
2249             * @param footerFormat the text, in {@code MessageFormat}, to be
2250             *        used as the footer, or {@code null} for no footer
2251             * @param showPrintDialog {@code true} to display a print dialog,
2252             *        {@code false} otherwise
2253             * @param service initial {@code PrintService}, or {@code null} for the
2254             *        default
2255             * @param attributes the job attributes to be applied to the print job, or
2256             *        {@code null} for none
2257             * @param interactive whether to print in an interactive mode
2258             * @return {@code true}, unless printing is canceled by the user
2259             * @throws PrinterException if an error in the print system causes the job
2260             *         to be aborted
2261             * @throws SecurityException if this thread is not allowed to
2262             *                           initiate a print job request
2263             * 
2264             * @see #getPrintable
2265             * @see java.text.MessageFormat
2266             * @see java.awt.GraphicsEnvironment#isHeadless
2267             * @see java.util.concurrent.FutureTask
2268             * 
2269             * @since 1.6
2270             */
2271            public boolean print(final MessageFormat headerFormat,
2272                    final MessageFormat footerFormat,
2273                    final boolean showPrintDialog, final PrintService service,
2274                    final PrintRequestAttributeSet attributes,
2275                    final boolean interactive) throws PrinterException {
2276
2277                final PrinterJob job = PrinterJob.getPrinterJob();
2278                final Printable printable;
2279                final PrintingStatus printingStatus;
2280                final boolean isHeadless = GraphicsEnvironment.isHeadless();
2281                final boolean isEventDispatchThread = SwingUtilities
2282                        .isEventDispatchThread();
2283                final Printable textPrintable = getPrintable(headerFormat,
2284                        footerFormat);
2285                if (interactive && !isHeadless) {
2286                    printingStatus = PrintingStatus.createPrintingStatus(this ,
2287                            job);
2288                    printable = printingStatus
2289                            .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) ? new HashPrintRequestAttributeSet()
2302                        : attributes;
2303
2304                if (showPrintDialog && !isHeadless && !job.printDialog(attr)) {
2305                    return false;
2306                }
2307
2308                /*
2309                 * there are three cases for printing:
2310                 * 1. print non interactively (! interactive || isHeadless)
2311                 * 2. print interactively off EDT
2312                 * 3. print interactively on EDT
2313                 * 
2314                 * 1 and 2 prints on the current thread (3 prints on another thread)
2315                 * 2 and 3 deal with PrintingStatusDialog
2316                 */
2317                final Callable<Object> doPrint = new Callable<Object>() {
2318                    public Object call() throws Exception {
2319                        try {
2320                            job.print(attr);
2321                        } finally {
2322                            if (printingStatus != null) {
2323                                printingStatus.dispose();
2324                            }
2325                        }
2326                        return null;
2327                    }
2328                };
2329
2330                final FutureTask<Object> futurePrinting = new FutureTask<Object>(
2331                        doPrint);
2332
2333                final Runnable runnablePrinting = new Runnable() {
2334                    public void run() {
2335                        //disable component
2336                        boolean wasEnabled = false;
2337                        if (isEventDispatchThread) {
2338                            if (isEnabled()) {
2339                                wasEnabled = true;
2340                                setEnabled(false);
2341                            }
2342                        } else {
2343                            try {
2344                                wasEnabled = SwingUtilities2.submit(
2345                                        new Callable<Boolean>() {
2346                                            public Boolean call()
2347                                                    throws Exception {
2348                                                boolean rv = isEnabled();
2349                                                if (rv) {
2350                                                    setEnabled(false);
2351                                                }
2352                                                return rv;
2353                                            }
2354                                        }).get();
2355                            } catch (InterruptedException e) {
2356                                throw new RuntimeException(e);
2357                            } catch (ExecutionException e) {
2358                                Throwable cause = e.getCause();
2359                                if (cause instanceof  Error) {
2360                                    throw (Error) cause;
2361                                }
2362                                if (cause instanceof  RuntimeException) {
2363                                    throw (RuntimeException) cause;
2364                                }
2365                                throw new AssertionError(cause);
2366                            }
2367                        }
2368
2369                        getDocument().render(futurePrinting);
2370
2371                        //enable component
2372                        if (wasEnabled) {
2373                            if (isEventDispatchThread) {
2374                                setEnabled(true);
2375                            } else {
2376                                try {
2377                                    SwingUtilities2.submit(new Runnable() {
2378                                        public void run() {
2379                                            setEnabled(true);
2380                                        }
2381                                    }, null).get();
2382                                } catch (InterruptedException e) {
2383                                    throw new RuntimeException(e);
2384                                } catch (ExecutionException e) {
2385                                    Throwable cause = e.getCause();
2386                                    if (cause instanceof  Error) {
2387                                        throw (Error) cause;
2388                                    }
2389                                    if (cause instanceof  RuntimeException) {
2390                                        throw (RuntimeException) cause;
2391                                    }
2392                                    throw new AssertionError(cause);
2393                                }
2394                            }
2395                        }
2396                    }
2397                };
2398
2399                if (!interactive || isHeadless) {
2400                    runnablePrinting.run();
2401                } else {
2402                    if (isEventDispatchThread) {
2403                        (new Thread(runnablePrinting)).start();
2404                        printingStatus.showModal(true);
2405                    } else {
2406                        printingStatus.showModal(false);
2407                        runnablePrinting.run();
2408                    }
2409                }
2410
2411                //the printing is done successfully or otherwise. 
2412                //dialog is hidden if needed.
2413                try {
2414                    futurePrinting.get();
2415                } catch (InterruptedException e) {
2416                    throw new RuntimeException(e);
2417                } catch (ExecutionException e) {
2418                    Throwable cause = e.getCause();
2419                    if (cause instanceof  PrinterAbortException) {
2420                        if (printingStatus != null
2421                                && printingStatus.isAborted()) {
2422                            return false;
2423                        } else {
2424                            throw (PrinterAbortException) cause;
2425                        }
2426                    } else if (cause instanceof  PrinterException) {
2427                        throw (PrinterException) cause;
2428                    } else if (cause instanceof  RuntimeException) {
2429                        throw (RuntimeException) cause;
2430                    } else if (cause instanceof  Error) {
2431                        throw (Error) cause;
2432                    } else {
2433                        throw new AssertionError(cause);
2434                    }
2435                }
2436                return true;
2437            }
2438
2439            /**
2440             * Returns a {@code Printable} to use for printing the content of this
2441             * {@code JTextComponent}. The returned {@code Printable} prints
2442             * the document as it looks on the screen except being reformatted
2443             * to fit the paper.
2444             * The returned {@code Printable} can be wrapped inside another
2445             * {@code Printable} in order to create complex reports and
2446             * documents.  
2447             *
2448             *
2449             * <p>
2450             * The returned {@code Printable} shares the {@code document} with this
2451             * {@code JTextComponent}. It is the responsibility of the developer to
2452             * ensure that the {@code document} is not mutated while this {@code Printable}
2453             * is used. Printing behavior is undefined when the {@code document} is
2454             * mutated during printing.
2455             * 
2456             * <p>
2457             * Page header and footer text can be added to the output by providing
2458             * {@code MessageFormat} arguments. The printing code requests
2459             * {@code Strings} from the formats, providing a single item which may be
2460             * included in the formatted string: an {@code Integer} representing the
2461             * current page number.
2462             *
2463             * <p> 
2464             * The returned {@code Printable} when printed, formats the
2465             * document content appropriately for the page size. For correct
2466             * line wrapping the {@code imageable width} of all pages must be the
2467             * same. See {@link java.awt.print.PageFormat#getImageableWidth}.
2468             *
2469             * <p>
2470             * This method is thread-safe, although most Swing methods are not. Please
2471             * see <A
2472             * HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">
2473             * How to Use Threads</A> for more information.
2474             *
2475             * <p>
2476             * The returned {@code Printable} can be printed on any thread.
2477             *
2478             * <p>
2479             * This implementation returned {@code Printable} performs all painting on
2480             * the <i>Event Dispatch Thread</i>, regardless of what thread it is
2481             * used on.
2482             * 
2483             * @param headerFormat the text, in {@code MessageFormat}, to be
2484             *        used as the header, or {@code null} for no header
2485             * @param footerFormat the text, in {@code MessageFormat}, to be
2486             *        used as the footer, or {@code null} for no footer
2487             * @return a {@code Printable} for use in printing content of this
2488             *         {@code JTextComponent}
2489             * 
2490             * 
2491             * @see java.awt.print.Printable
2492             * @see java.awt.print.PageFormat
2493             * @see javax.swing.text.Document#render(java.lang.Runnable)
2494             *
2495             * @since 1.6
2496             */
2497            public Printable getPrintable(final MessageFormat headerFormat,
2498                    final MessageFormat footerFormat) {
2499                return TextComponentPrintable.getPrintable(this , headerFormat,
2500                        footerFormat);
2501            }
2502
2503            /////////////////
2504            // Accessibility support
2505            ////////////////
2506
2507            /**
2508             * Gets the <code>AccessibleContext</code> associated with this
2509             * <code>JTextComponent</code>. For text components,
2510             * the <code>AccessibleContext</code> takes the form of an 
2511             * <code>AccessibleJTextComponent</code>. 
2512             * A new <code>AccessibleJTextComponent</code> instance
2513             * is created if necessary.
2514             *
2515             * @return an <code>AccessibleJTextComponent</code> that serves as the 
2516             *         <code>AccessibleContext</code> of this
2517             *	       <code>JTextComponent</code>
2518             */
2519            public AccessibleContext getAccessibleContext() {
2520                if (accessibleContext == null) {
2521                    accessibleContext = new AccessibleJTextComponent();
2522                }
2523                return accessibleContext;
2524            }
2525
2526            /**
2527             * This class implements accessibility support for the 
2528             * <code>JTextComponent</code> class.  It provides an implementation of 
2529             * the Java Accessibility API appropriate to menu user-interface elements.
2530             * <p>
2531             * <strong>Warning:</strong>
2532             * Serialized objects of this class will not be compatible with
2533             * future Swing releases. The current serialization support is
2534             * appropriate for short term storage or RMI between applications running
2535             * the same version of Swing.  As of 1.4, support for long term storage
2536             * of all JavaBeans<sup><font size="-2">TM</font></sup>
2537             * has been added to the <code>java.beans</code> package.
2538             * Please see {@link java.beans.XMLEncoder}.
2539             */
2540            public class AccessibleJTextComponent extends AccessibleJComponent
2541                    implements  AccessibleText, CaretListener, DocumentListener,
2542                    AccessibleAction, AccessibleEditableText,
2543                    AccessibleExtendedText {
2544
2545                int caretPos;
2546                Point oldLocationOnScreen;
2547
2548                /**
2549                 * Constructs an AccessibleJTextComponent.  Adds a listener to track
2550                 * caret change.
2551                 */
2552                public AccessibleJTextComponent() {
2553                    Document doc = JTextComponent.this .getDocument();
2554                    if (doc != null) {
2555                        doc.addDocumentListener(this );
2556                    }
2557                    JTextComponent.this .addCaretListener(this );
2558                    caretPos = getCaretPosition();
2559
2560                    try {
2561                        oldLocationOnScreen = getLocationOnScreen();
2562                    } catch (IllegalComponentStateException iae) {
2563                    }
2564
2565                    // Fire a ACCESSIBLE_VISIBLE_DATA_PROPERTY PropertyChangeEvent
2566                    // when the text component moves (e.g., when scrolling).
2567                    // Using an anonymous class since making AccessibleJTextComponent
2568                    // implement ComponentListener would be an API change.
2569                    JTextComponent.this 
2570                            .addComponentListener(new ComponentAdapter() {
2571
2572                                public void componentMoved(ComponentEvent e) {
2573                                    try {
2574                                        Point newLocationOnScreen = getLocationOnScreen();
2575                                        firePropertyChange(
2576                                                ACCESSIBLE_VISIBLE_DATA_PROPERTY,
2577                                                oldLocationOnScreen,
2578                                                newLocationOnScreen);
2579
2580                                        oldLocationOnScreen = newLocationOnScreen;
2581                                    } catch (IllegalComponentStateException iae) {
2582                                    }
2583                                }
2584                            });
2585                }
2586
2587                /**
2588                 * Handles caret updates (fire appropriate property change event,
2589                 * which are AccessibleContext.ACCESSIBLE_CARET_PROPERTY and
2590                 * AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY).
2591                 * This keeps track of the dot position internally.  When the caret
2592                 * moves, the internal position is updated after firing the event.
2593                 *
2594                 * @param e the CaretEvent
2595                 */
2596                public void caretUpdate(CaretEvent e) {
2597                    int dot = e.getDot();
2598                    int mark = e.getMark();
2599                    if (caretPos != dot) {
2600                        // the caret moved
2601                        firePropertyChange(ACCESSIBLE_CARET_PROPERTY,
2602                                new Integer(caretPos), new Integer(dot));
2603                        caretPos = dot;
2604
2605                        try {
2606                            oldLocationOnScreen = getLocationOnScreen();
2607                        } catch (IllegalComponentStateException iae) {
2608                        }
2609                    }
2610                    if (mark != dot) {
2611                        // there is a selection
2612                        firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
2613                                getSelectedText());
2614                    }
2615                }
2616
2617                // DocumentListener methods
2618
2619                /**
2620                 * Handles document insert (fire appropriate property change event
2621                 * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
2622                 * This tracks the changed offset via the event.
2623                 *
2624                 * @param e the DocumentEvent
2625                 */
2626                public void insertUpdate(DocumentEvent e) {
2627                    final Integer pos = new Integer(e.getOffset());
2628                    if (SwingUtilities.isEventDispatchThread()) {
2629                        firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
2630                    } else {
2631                        Runnable doFire = new Runnable() {
2632                            public void run() {
2633                                firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
2634                                        null, pos);
2635                            }
2636                        };
2637                        SwingUtilities.invokeLater(doFire);
2638                    }
2639                }
2640
2641                /**
2642                 * Handles document remove (fire appropriate property change event,
2643                 * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
2644                 * This tracks the changed offset via the event.
2645                 *
2646                 * @param e the DocumentEvent
2647                 */
2648                public void removeUpdate(DocumentEvent e) {
2649                    final Integer pos = new Integer(e.getOffset());
2650                    if (SwingUtilities.isEventDispatchThread()) {
2651                        firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
2652                    } else {
2653                        Runnable doFire = new Runnable() {
2654                            public void run() {
2655                                firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
2656                                        null, pos);
2657                            }
2658                        };
2659                        SwingUtilities.invokeLater(doFire);
2660                    }
2661                }
2662
2663                /**
2664                 * Handles document remove (fire appropriate property change event,
2665                 * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
2666                 * This tracks the changed offset via the event.
2667                 *
2668                 * @param e the DocumentEvent
2669                 */
2670                public void changedUpdate(DocumentEvent e) {
2671                    final Integer pos = new Integer(e.getOffset());
2672                    if (SwingUtilities.isEventDispatchThread()) {
2673                        firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
2674                    } else {
2675                        Runnable doFire = new Runnable() {
2676                            public void run() {
2677                                firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
2678                                        null, pos);
2679                            }
2680                        };
2681                        SwingUtilities.invokeLater(doFire);
2682                    }
2683                }
2684
2685                /**
2686                 * Gets the state set of the JTextComponent.  
2687                 * The AccessibleStateSet of an object is composed of a set of 
2688                 * unique AccessibleState's.  A change in the AccessibleStateSet 
2689                 * of an object will cause a PropertyChangeEvent to be fired
2690                 * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
2691                 *
2692                 * @return an instance of AccessibleStateSet containing the
2693                 * current state set of the object
2694                 * @see AccessibleStateSet
2695                 * @see AccessibleState
2696                 * @see #addPropertyChangeListener
2697                 */
2698                public AccessibleStateSet getAccessibleStateSet() {
2699                    AccessibleStateSet states = super .getAccessibleStateSet();
2700                    if (JTextComponent.this .isEditable()) {
2701                        states.add(AccessibleState.EDITABLE);
2702                    }
2703                    return states;
2704                }
2705
2706                /**
2707                 * Gets the role of this object.
2708                 *
2709                 * @return an instance of AccessibleRole describing the role of the 
2710                 * object (AccessibleRole.TEXT)
2711                 * @see AccessibleRole
2712                 */
2713                public AccessibleRole getAccessibleRole() {
2714                    return AccessibleRole.TEXT;
2715                }
2716
2717                /**
2718                 * Get the AccessibleText associated with this object.  In the
2719                 * implementation of the Java Accessibility API for this class, 
2720                 * return this object, which is responsible for implementing the
2721                 * AccessibleText interface on behalf of itself.
2722                 * 
2723                 * @return this object
2724                 */
2725                public AccessibleText getAccessibleText() {
2726                    return this ;
2727                }
2728
2729                // --- interface AccessibleText methods ------------------------
2730
2731                /**
2732                 * Many of these methods are just convenience methods; they
2733                 * just call the equivalent on the parent
2734                 */
2735
2736                /**
2737                 * Given a point in local coordinates, return the zero-based index
2738                 * of the character under that Point.  If the point is invalid,
2739                 * this method returns -1.
2740                 *
2741                 * @param p the Point in local coordinates
2742                 * @return the zero-based index of the character under Point p.
2743                 */
2744                public int getIndexAtPoint(Point p) {
2745                    if (p == null) {
2746                        return -1;
2747                    }
2748                    return JTextComponent.this .viewToModel(p);
2749                }
2750
2751                /**
2752                 * Gets the editor's drawing rectangle.  Stolen
2753                 * from the unfortunately named 
2754                 * BasicTextUI.getVisibleEditorRect()
2755                 *
2756                 * @return the bounding box for the root view
2757                 */
2758                Rectangle getRootEditorRect() {
2759                    Rectangle alloc = JTextComponent.this .getBounds();
2760                    if ((alloc.width > 0) && (alloc.height > 0)) {
2761                        alloc.x = alloc.y = 0;
2762                        Insets insets = JTextComponent.this .getInsets();
2763                        alloc.x += insets.left;
2764                        alloc.y += insets.top;
2765                        alloc.width -= insets.left + insets.right;
2766                        alloc.height -= insets.top + insets.bottom;
2767                        return alloc;
2768                    }
2769                    return null;
2770                }
2771
2772                /**
2773                 * Determines the bounding box of the character at the given
2774                 * index into the string.  The bounds are returned in local
2775                 * coordinates.  If the index is invalid a null rectangle
2776                 * is returned.
2777                 *
2778                 * The screen coordinates returned are "unscrolled coordinates"
2779                 * if the JTextComponent is contained in a JScrollPane in which
2780                 * case the resulting rectangle should be composed with the parent
2781                 * coordinates.  A good algorithm to use is:
2782                 * <nf>
2783                 * Accessible a:
2784                 * AccessibleText at = a.getAccessibleText();
2785                 * AccessibleComponent ac = a.getAccessibleComponent();
2786                 * Rectangle r = at.getCharacterBounds();
2787                 * Point p = ac.getLocation();
2788                 * r.x += p.x;
2789                 * r.y += p.y;
2790                 * </nf>
2791                 *
2792                 * Note: the JTextComponent must have a valid size (e.g. have
2793                 * been added to a parent container whose ancestor container
2794                 * is a valid top-level window) for this method to be able
2795                 * to return a meaningful (non-null) value.
2796                 *
2797                 * @param i the index into the String >= 0
2798                 * @return the screen coordinates of the character's bounding box
2799                 */
2800                public Rectangle getCharacterBounds(int i) {
2801                    if (i < 0 || i > model.getLength() - 1) {
2802                        return null;
2803                    }
2804                    TextUI ui = getUI();
2805                    if (ui == null) {
2806                        return null;
2807                    }
2808                    Rectangle rect = null;
2809                    Rectangle alloc = getRootEditorRect();
2810                    if (alloc == null) {
2811                        return null;
2812                    }
2813                    if (model instanceof  AbstractDocument) {
2814                        ((AbstractDocument) model).readLock();
2815                    }
2816                    try {
2817                        View rootView = ui.getRootView(JTextComponent.this );
2818                        if (rootView != null) {
2819                            rootView.setSize(alloc.width, alloc.height);
2820
2821                            Shape bounds = rootView.modelToView(i,
2822                                    Position.Bias.Forward, i + 1,
2823                                    Position.Bias.Backward, alloc);
2824
2825                            rect = (bounds instanceof  Rectangle) ? (Rectangle) bounds
2826                                    : bounds.getBounds();
2827
2828                        }
2829                    } catch (BadLocationException e) {
2830                    } finally {
2831                        if (model instanceof  AbstractDocument) {
2832                            ((AbstractDocument) model).readUnlock();
2833                        }
2834                    }
2835                    return rect;
2836                }
2837
2838                /**
2839                 * Returns the number of characters (valid indices)
2840                 *
2841                 * @return the number of characters >= 0
2842                 */
2843                public int getCharCount() {
2844                    return model.getLength();
2845                }
2846
2847                /**
2848                 * Returns the zero-based offset of the caret.
2849                 *
2850                 * Note: The character to the right of the caret will have the
2851                 * same index value as the offset (the caret is between
2852                 * two characters).
2853                 *
2854                 * @return the zero-based offset of the caret.
2855                 */
2856                public int getCaretPosition() {
2857                    return JTextComponent.this .getCaretPosition();
2858                }
2859
2860                /**
2861                 * Returns the AttributeSet for a given character (at a given index).
2862                 *
2863                 * @param i the zero-based index into the text
2864                 * @return the AttributeSet of the character
2865                 */
2866                public AttributeSet getCharacterAttribute(int i) {
2867                    Element e = null;
2868                    if (model instanceof  AbstractDocument) {
2869                        ((AbstractDocument) model).readLock();
2870                    }
2871                    try {
2872                        for (e = model.getDefaultRootElement(); !e.isLeaf();) {
2873                            int index = e.getElementIndex(i);
2874                            e = e.getElement(index);
2875                        }
2876                    } finally {
2877                        if (model instanceof  AbstractDocument) {
2878                            ((AbstractDocument) model).readUnlock();
2879                        }
2880                    }
2881                    return e.getAttributes();
2882                }
2883
2884                /**
2885                 * Returns the start offset within the selected text.
2886                 * If there is no selection, but there is
2887                 * a caret, the start and end offsets will be the same.
2888                 * Return 0 if the text is empty, or the caret position
2889                 * if no selection.
2890                 *
2891                 * @return the index into the text of the start of the selection >= 0
2892                 */
2893                public int getSelectionStart() {
2894                    return JTextComponent.this .getSelectionStart();
2895                }
2896
2897                /**
2898                 * Returns the end offset within the selected text.
2899                 * If there is no selection, but there is
2900                 * a caret, the start and end offsets will be the same.
2901                 * Return 0 if the text is empty, or the caret position
2902                 * if no selection.
2903                 *
2904                 * @return the index into teh text of the end of the selection >= 0
2905                 */
2906                public int getSelectionEnd() {
2907                    return JTextComponent.this .getSelectionEnd();
2908                }
2909
2910                /**
2911                 * Returns the portion of the text that is selected.
2912                 *
2913                 * @return the text, null if no selection
2914                 */
2915                public String getSelectedText() {
2916                    return JTextComponent.this .getSelectedText();
2917                }
2918
2919                /**
2920                 * IndexedSegment extends Segment adding the offset into the
2921                 * the model the <code>Segment</code> was asked for.
2922                 */
2923                private class IndexedSegment extends Segment {
2924                    /**
2925                     * Offset into the model that the position represents.
2926                     */
2927                    public int modelOffset;
2928                }
2929
2930                // TIGER - 4170173
2931                /**
2932                 * Returns the String at a given index. Whitespace
2933                 * between words is treated as a word.
2934                 *
2935                 * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2936                 * @param index an index within the text
2937                 * @return the letter, word, or sentence.
2938                 *
2939                 */
2940                public String getAtIndex(int part, int index) {
2941                    return getAtIndex(part, index, 0);
2942                }
2943
2944                /**
2945                 * Returns the String after a given index. Whitespace
2946                 * between words is treated as a word.
2947                 *
2948                 * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2949                 * @param index an index within the text
2950                 * @return the letter, word, or sentence.
2951                 */
2952                public String getAfterIndex(int part, int index) {
2953                    return getAtIndex(part, index, 1);
2954                }
2955
2956                /**
2957                 * Returns the String before a given index. Whitespace
2958                 * between words is treated a word.
2959                 *
2960                 * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2961                 * @param index an index within the text
2962                 * @return the letter, word, or sentence.
2963                 */
2964                public String getBeforeIndex(int part, int index) {
2965                    return getAtIndex(part, index, -1);
2966                }
2967
2968                /**
2969                 * Gets the word, sentence, or character at <code>index</code>.
2970                 * If <code>direction</code> is non-null this will find the
2971                 * next/previous word/sentence/character.
2972                 */
2973                private String getAtIndex(int part, int index, int direction) {
2974                    if (model instanceof  AbstractDocument) {
2975                        ((AbstractDocument) model).readLock();
2976                    }
2977                    try {
2978                        if (index < 0 || index >= model.getLength()) {
2979                            return null;
2980                        }
2981                        switch (part) {
2982                        case AccessibleText.CHARACTER:
2983                            if (index + direction < model.getLength()
2984                                    && index + direction >= 0) {
2985                                return model.getText(index + direction, 1);
2986                            }
2987                            break;
2988
2989                        case AccessibleText.WORD:
2990                        case AccessibleText.SENTENCE:
2991                            IndexedSegment seg = getSegmentAt(part, index);
2992                            if (seg != null) {
2993                                if (direction != 0) {
2994                                    int next;
2995
2996                                    if (direction < 0) {
2997                                        next = seg.modelOffset - 1;
2998                                    } else {
2999                                        next = seg.modelOffset + direction
3000                                                * seg.count;
3001                                    }
3002                                    if (next >= 0 && next <= model.getLength()) {
3003                                        seg = getSegmentAt(part, next);
3004                                    } else {
3005                                        seg = null;
3006                                    }
3007                                }
3008                                if (seg != null) {
3009                                    return new String(seg.array, seg.offset,
3010                                            seg.count);
3011                                }
3012                            }
3013                            break;
3014
3015                        default:
3016                            break;
3017                        }
3018                    } catch (BadLocationException e) {
3019                    } finally {
3020                        if (model instanceof  AbstractDocument) {
3021                            ((AbstractDocument) model).readUnlock();
3022                        }
3023                    }
3024                    return null;
3025                }
3026
3027                /*
3028                 * Returns the paragraph element for the specified index.
3029                 */
3030                private Element getParagraphElement(int index) {
3031                    if (model instanceof  PlainDocument) {
3032                        PlainDocument sdoc = (PlainDocument) model;
3033                        return sdoc.getParagraphElement(index);
3034                    } else if (model instanceof  StyledDocument) {
3035                        StyledDocument sdoc = (StyledDocument) model;
3036                        return sdoc.getParagraphElement(index);
3037                    } else {
3038                        Element para = null;
3039                        for (para = model.getDefaultRootElement(); !para
3040                                .isLeaf();) {
3041                            int pos = para.getElementIndex(index);
3042                            para = para.getElement(pos);
3043                        }
3044                        if (para == null) {
3045                            return null;
3046                        }
3047                        return para.getParentElement();
3048                    }
3049                }
3050
3051                /*
3052                 * Returns a <code>Segment</code> containing the paragraph text
3053                 * at <code>index</code>, or null if <code>index</code> isn't
3054                 * valid.
3055                 */
3056                private IndexedSegment getParagraphElementText(int index)
3057                        throws BadLocationException {
3058                    Element para = getParagraphElement(index);
3059
3060                    if (para != null) {
3061                        IndexedSegment segment = new IndexedSegment();
3062                        try {
3063                            int length = para.getEndOffset()
3064                                    - para.getStartOffset();
3065                            model.getText(para.getStartOffset(), length,
3066                                    segment);
3067                        } catch (BadLocationException e) {
3068                            return null;
3069                        }
3070                        segment.modelOffset = para.getStartOffset();
3071                        return segment;
3072                    }
3073                    return null;
3074                }
3075
3076                /**
3077                 * Returns the Segment at <code>index</code> representing either
3078                 * the paragraph or sentence as identified by <code>part</code>, or
3079                 * null if a valid paragraph/sentence can't be found. The offset
3080                 * will point to the start of the word/sentence in the array, and
3081                 * the modelOffset will point to the location of the word/sentence
3082                 * in the model.
3083                 */
3084                private IndexedSegment getSegmentAt(int part, int index)
3085                        throws BadLocationException {
3086                    IndexedSegment seg = getParagraphElementText(index);
3087                    if (seg == null) {
3088                        return null;
3089                    }
3090                    BreakIterator iterator;
3091                    switch (part) {
3092                    case AccessibleText.WORD:
3093                        iterator = BreakIterator.getWordInstance(getLocale());
3094                        break;
3095                    case AccessibleText.SENTENCE:
3096                        iterator = BreakIterator
3097                                .getSentenceInstance(getLocale());
3098                        break;
3099                    default:
3100                        return null;
3101                    }
3102                    seg.first();
3103                    iterator.setText(seg);
3104                    int end = iterator.following(index - seg.modelOffset
3105                            + seg.offset);
3106                    if (end == BreakIterator.DONE) {
3107                        return null;
3108                    }
3109                    if (end > seg.offset + seg.count) {
3110                        return null;
3111                    }
3112                    int begin = iterator.previous();
3113                    if (begin == BreakIterator.DONE
3114                            || begin >= seg.offset + seg.count) {
3115                        return null;
3116                    }
3117                    seg.modelOffset = seg.modelOffset + begin - seg.offset;
3118                    seg.offset = begin;
3119                    seg.count = end - begin;
3120                    return seg;
3121                }
3122
3123                // begin AccessibleEditableText methods -----
3124
3125                /**
3126                 * Returns the AccessibleEditableText interface for
3127                 * this text component.
3128                 *
3129                 * @return the AccessibleEditableText interface
3130                 * @since 1.4
3131                 */
3132                public AccessibleEditableText getAccessibleEditableText() {
3133                    return this ;
3134                }
3135
3136                /**
3137                 * Sets the text contents to the specified string.
3138                 *
3139                 * @param s the string to set the text contents
3140                 * @since 1.4
3141                 */
3142                public void setTextContents(String s) {
3143                    JTextComponent.this .setText(s);
3144                }
3145
3146                /**
3147                 * Inserts the specified string at the given index
3148                 *
3149                 * @param index the index in the text where the string will 
3150                 * be inserted
3151                 * @param s the string to insert in the text
3152                 * @since 1.4
3153                 */
3154                public void insertTextAtIndex(int index, String s) {
3155                    Document doc = JTextComponent.this .getDocument();
3156                    if (doc != null) {
3157                        try {
3158                            if (s != null && s.length() > 0) {
3159                                boolean composedTextSaved = saveComposedText(index);
3160                                doc.insertString(index, s, null);
3161                                if (composedTextSaved) {
3162                                    restoreComposedText();
3163                                }
3164                            }
3165                        } catch (BadLocationException e) {
3166                            UIManager.getLookAndFeel().provideErrorFeedback(
3167                                    JTextComponent.this );
3168                        }
3169                    }
3170                }
3171
3172                /**
3173                 * Returns the text string between two indices.
3174                 * 
3175                 * @param startIndex the starting index in the text
3176                 * @param endIndex the ending index in the text
3177                 * @return the text string between the indices
3178                 * @since 1.4
3179                 */
3180                public String getTextRange(int startIndex, int endIndex) {
3181                    String txt = null;
3182                    int p0 = Math.min(startIndex, endIndex);
3183                    int p1 = Math.max(startIndex, endIndex);
3184                    if (p0 != p1) {
3185                        try {
3186                            Document doc = JTextComponent.this .getDocument();
3187                            txt = doc.getText(p0, p1 - p0);
3188                        } catch (BadLocationException e) {
3189                            throw new IllegalArgumentException(e.getMessage());
3190                        }
3191                    }
3192                    return txt;
3193                }
3194
3195                /**
3196                 * Deletes the text between two indices
3197                 *
3198                 * @param startIndex the starting index in the text
3199                 * @param endIndex the ending index in the text
3200                 * @since 1.4
3201                 */
3202                public void delete(int startIndex, int endIndex) {
3203                    if (isEditable() && isEnabled()) {
3204                        try {
3205                            int p0 = Math.min(startIndex, endIndex);
3206                            int p1 = Math.max(startIndex, endIndex);
3207                            if (p0 != p1) {
3208                                Document doc = getDocument();
3209                                doc.remove(p0, p1 - p0);
3210                            }
3211                        } catch (BadLocationException e) {
3212                        }
3213                    } else {
3214                        UIManager.getLookAndFeel().provideErrorFeedback(
3215                                JTextComponent.this );
3216                    }
3217                }
3218
3219                /**
3220                 * Cuts the text between two indices into the system clipboard.
3221                 *
3222                 * @param startIndex the starting index in the text
3223                 * @param endIndex the ending index in the text
3224                 * @since 1.4
3225                 */
3226                public void cut(int startIndex, int endIndex) {
3227                    selectText(startIndex, endIndex);
3228                    JTextComponent.this .cut();
3229                }
3230
3231                /**
3232                 * Pastes the text from the system clipboard into the text
3233                 * starting at the specified index.
3234                 *
3235                 * @param startIndex the starting index in the text
3236                 * @since 1.4
3237                 */
3238                public void paste(int startIndex) {
3239                    setCaretPosition(startIndex);
3240                    JTextComponent.this .paste();
3241                }
3242
3243                /**
3244                 * Replaces the text between two indices with the specified
3245                 * string.
3246                 *
3247                 * @param startIndex the starting index in the text
3248                 * @param endIndex the ending index in the text
3249                 * @param s the string to replace the text between two indices
3250                 * @since 1.4
3251                 */
3252                public void replaceText(int startIndex, int endIndex, String s) {
3253                    selectText(startIndex, endIndex);
3254                    JTextComponent.this .replaceSelection(s);
3255                }
3256
3257                /**
3258                 * Selects the text between two indices.
3259                 *
3260                 * @param startIndex the starting index in the text
3261                 * @param endIndex the ending index in the text
3262                 * @since 1.4
3263                 */
3264                public void selectText(int startIndex, int endIndex) {
3265                    JTextComponent.this .select(startIndex, endIndex);
3266                }
3267
3268                /**
3269                 * Sets attributes for 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                 * @param as the attribute set
3274                 * @see AttributeSet
3275                 * @since 1.4
3276                 */
3277                public void setAttributes(int startIndex, int endIndex,
3278                        AttributeSet as) {
3279
3280                    // Fixes bug 4487492
3281                    Document doc = JTextComponent.this .getDocument();
3282                    if (doc != null && doc instanceof  StyledDocument) {
3283                        StyledDocument sDoc = (StyledDocument) doc;
3284                        int offset = startIndex;
3285                        int length = endIndex - startIndex;
3286                        sDoc.setCharacterAttributes(offset, length, as, true);
3287                    }
3288                }
3289
3290                // ----- end AccessibleEditableText methods
3291
3292                // ----- begin AccessibleExtendedText methods
3293
3294                // Probably should replace the helper method getAtIndex() to return
3295                // instead an AccessibleTextSequence also for LINE & ATTRIBUTE_RUN
3296                // and then make the AccessibleText methods get[At|After|Before]Point
3297                // call this new method instead and return only the string portion
3298
3299                /**
3300                 * Returns the AccessibleTextSequence at a given <code>index</code>.
3301                 * If <code>direction</code> is non-null this will find the
3302                 * next/previous word/sentence/character.
3303                 *
3304                 * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3305                 * <code>SENTENCE</code>, <code>LINE</code> or
3306                 * <code>ATTRIBUTE_RUN</code> to retrieve
3307                 * @param index an index within the text
3308                 * @param direction is either -1, 0, or 1
3309                 * @return an <code>AccessibleTextSequence</code> specifying the text
3310                 * if <code>part</code> and <code>index</code> are valid.  Otherwise,
3311                 * <code>null</code> is returned.
3312                 *
3313                 * @see javax.accessibility.AccessibleText#CHARACTER
3314                 * @see javax.accessibility.AccessibleText#WORD
3315                 * @see javax.accessibility.AccessibleText#SENTENCE
3316                 * @see javax.accessibility.AccessibleExtendedText#LINE
3317                 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3318                 *
3319                 * @since 1.6
3320                 */
3321                private AccessibleTextSequence getSequenceAtIndex(int part,
3322                        int index, int direction) {
3323                    if (index < 0 || index >= model.getLength()) {
3324                        return null;
3325                    }
3326                    if (direction < -1 || direction > 1) {
3327                        return null; // direction must be 1, 0, or -1
3328                    }
3329
3330                    switch (part) {
3331                    case AccessibleText.CHARACTER:
3332                        if (model instanceof  AbstractDocument) {
3333                            ((AbstractDocument) model).readLock();
3334                        }
3335                        AccessibleTextSequence charSequence = null;
3336                        try {
3337                            if (index + direction < model.getLength()
3338                                    && index + direction >= 0) {
3339                                charSequence = new AccessibleTextSequence(index
3340                                        + direction, index + direction + 1,
3341                                        model.getText(index + direction, 1));
3342                            }
3343
3344                        } catch (BadLocationException e) {
3345                            // we are intentionally silent; our contract says we return
3346                            // null if there is any failure in this method
3347                        } finally {
3348                            if (model instanceof  AbstractDocument) {
3349                                ((AbstractDocument) model).readUnlock();
3350                            }
3351                        }
3352                        return charSequence;
3353
3354                    case AccessibleText.WORD:
3355                    case AccessibleText.SENTENCE:
3356                        if (model instanceof  AbstractDocument) {
3357                            ((AbstractDocument) model).readLock();
3358                        }
3359                        AccessibleTextSequence rangeSequence = null;
3360                        try {
3361                            IndexedSegment seg = getSegmentAt(part, index);
3362                            if (seg != null) {
3363                                if (direction != 0) {
3364                                    int next;
3365
3366                                    if (direction < 0) {
3367                                        next = seg.modelOffset - 1;
3368                                    } else {
3369                                        next = seg.modelOffset + seg.count;
3370                                    }
3371                                    if (next >= 0 && next <= model.getLength()) {
3372                                        seg = getSegmentAt(part, next);
3373                                    } else {
3374                                        seg = null;
3375                                    }
3376                                }
3377                                if (seg != null
3378                                        && (seg.offset + seg.count) <= model
3379                                                .getLength()) {
3380                                    rangeSequence = new AccessibleTextSequence(
3381                                            seg.offset, seg.offset + seg.count,
3382                                            new String(seg.array, seg.offset,
3383                                                    seg.count));
3384                                } // else we leave rangeSequence set to null
3385                            }
3386                        } catch (BadLocationException e) {
3387                            // we are intentionally silent; our contract says we return
3388                            // null if there is any failure in this method
3389                        } finally {
3390                            if (model instanceof  AbstractDocument) {
3391                                ((AbstractDocument) model).readUnlock();
3392                            }
3393                        }
3394                        return rangeSequence;
3395
3396                    case AccessibleExtendedText.LINE:
3397                        AccessibleTextSequence lineSequence = null;
3398                        if (model instanceof  AbstractDocument) {
3399                            ((AbstractDocument) model).readLock();
3400                        }
3401                        try {
3402                            int startIndex = Utilities.getRowStart(
3403                                    JTextComponent.this , index);
3404                            int endIndex = Utilities.getRowEnd(
3405                                    JTextComponent.this , index);
3406                            if (startIndex >= 0 && endIndex >= startIndex) {
3407                                if (direction == 0) {
3408                                    lineSequence = new AccessibleTextSequence(
3409                                            startIndex, endIndex,
3410                                            model.getText(startIndex, endIndex
3411                                                    - startIndex + 1));
3412                                } else if (direction == -1 && startIndex > 0) {
3413                                    endIndex = Utilities
3414                                            .getRowEnd(JTextComponent.this ,
3415                                                    startIndex - 1);
3416                                    startIndex = Utilities
3417                                            .getRowStart(JTextComponent.this ,
3418                                                    startIndex - 1);
3419                                    if (startIndex >= 0
3420                                            && endIndex >= startIndex) {
3421                                        lineSequence = new AccessibleTextSequence(
3422                                                startIndex, endIndex,
3423                                                model.getText(startIndex,
3424                                                        endIndex - startIndex
3425                                                                + 1));
3426                                    }
3427                                } else if (direction == 1
3428                                        && endIndex < model.getLength()) {
3429                                    startIndex = Utilities.getRowStart(
3430                                            JTextComponent.this , endIndex + 1);
3431                                    endIndex = Utilities.getRowEnd(
3432                                            JTextComponent.this , endIndex + 1);
3433                                    if (startIndex >= 0
3434                                            && endIndex >= startIndex) {
3435                                        lineSequence = new AccessibleTextSequence(
3436                                                startIndex, endIndex,
3437                                                model.getText(startIndex,
3438                                                        endIndex - startIndex
3439                                                                + 1));
3440                                    }
3441                                }
3442                                // already validated 'direction' above...
3443                            }
3444                        } catch (BadLocationException e) {
3445                            // we are intentionally silent; our contract says we return
3446                            // null if there is any failure in this method
3447                        } finally {
3448                            if (model instanceof  AbstractDocument) {
3449                                ((AbstractDocument) model).readUnlock();
3450                            }
3451                        }
3452                        return lineSequence;
3453
3454                    case AccessibleExtendedText.ATTRIBUTE_RUN:
3455                        // assumptions: (1) that all characters in a single element 
3456                        // share the same attribute set; (2) that adjacent elements
3457                        // *may* share the same attribute set
3458
3459                        int attributeRunStartIndex,
3460                        attributeRunEndIndex;
3461                        String runText = null;
3462                        if (model instanceof  AbstractDocument) {
3463                            ((AbstractDocument) model).readLock();
3464                        }
3465
3466                        try {
3467                            attributeRunStartIndex = attributeRunEndIndex = Integer.MIN_VALUE;
3468                            int tempIndex = index;
3469                            switch (direction) {
3470                            case -1:
3471                                // going backwards, so find left edge of this run - 
3472                                // that'll be the end of the previous run 
3473                                // (off-by-one counting)
3474                                attributeRunEndIndex = getRunEdge(index,
3475                                        direction);
3476                                // now set ourselves up to find the left edge of the 
3477                                // prev. run
3478                                tempIndex = attributeRunEndIndex - 1;
3479                                break;
3480                            case 1:
3481                                // going forward, so find right edge of this run -
3482                                // that'll be the start of the next run 
3483                                // (off-by-one counting)
3484                                attributeRunStartIndex = getRunEdge(index,
3485                                        direction);
3486                                // now set ourselves up to find the right edge of the 
3487                                // next run
3488                                tempIndex = attributeRunStartIndex;
3489                                break;
3490                            case 0:
3491                                // interested in the current run, so nothing special to 
3492                                // set up in advance...
3493                                break;
3494                            default:
3495                                // only those three values of direction allowed...
3496                                throw new AssertionError(direction);
3497                            }
3498
3499                            // set the unset edge; if neither set then we're getting 
3500                            // both edges of the current run around our 'index'
3501                            attributeRunStartIndex = (attributeRunStartIndex != Integer.MIN_VALUE) ? attributeRunStartIndex
3502                                    : getRunEdge(tempIndex, -1);
3503                            attributeRunEndIndex = (attributeRunEndIndex != Integer.MIN_VALUE) ? attributeRunEndIndex
3504                                    : getRunEdge(tempIndex, 1);
3505
3506                            runText = model.getText(attributeRunStartIndex,
3507                                    attributeRunEndIndex
3508                                            - attributeRunStartIndex);
3509                        } catch (BadLocationException e) {
3510                            // we are intentionally silent; our contract says we return
3511                            // null if there is any failure in this method
3512                            return null;
3513                        } finally {
3514                            if (model instanceof  AbstractDocument) {
3515                                ((AbstractDocument) model).readUnlock();
3516                            }
3517                        }
3518                        return new AccessibleTextSequence(
3519                                attributeRunStartIndex, attributeRunEndIndex,
3520                                runText);
3521
3522                    default:
3523                        break;
3524                    }
3525                    return null;
3526                }
3527
3528                /**
3529                 * Starting at text position <code>index</code>, and going in 
3530                 * <code>direction</code>, return the edge of run that shares the 
3531                 * same <code>AttributeSet</code> and parent element as those at 
3532                 * <code>index</code>.
3533                 *
3534                 * Note: we assume the document is already locked...
3535                 */
3536                private int getRunEdge(int index, int direction)
3537                        throws BadLocationException {
3538                    if (index < 0 || index >= model.getLength()) {
3539                        throw new BadLocationException(
3540                                "Location out of bounds", index);
3541                    }
3542                    // locate the Element at index
3543                    Element indexElement = null;
3544                    // locate the Element at our index/offset
3545                    int elementIndex = -1; // test for initialization
3546                    for (indexElement = model.getDefaultRootElement(); !indexElement
3547                            .isLeaf();) {
3548                        elementIndex = indexElement.getElementIndex(index);
3549                        indexElement = indexElement.getElement(elementIndex);
3550                    }
3551                    if (elementIndex == -1) {
3552                        throw new AssertionError(index);
3553                    }
3554                    // cache the AttributeSet and parentElement atindex 
3555                    AttributeSet indexAS = indexElement.getAttributes();
3556                    Element parent = indexElement.getParentElement();
3557
3558                    // find the first Element before/after ours w/the same AttributeSet
3559                    // if we are already at edge of the first element in our parent
3560                    // then return that edge
3561                    Element edgeElement = indexElement;
3562                    switch (direction) {
3563                    case -1:
3564                    case 1:
3565                        int edgeElementIndex = elementIndex;
3566                        int elementCount = parent.getElementCount();
3567                        while ((edgeElementIndex + direction) > 0
3568                                && ((edgeElementIndex + direction) < elementCount)
3569                                && parent.getElement(
3570                                        edgeElementIndex + direction)
3571                                        .getAttributes().isEqual(indexAS)) {
3572                            edgeElementIndex += direction;
3573                        }
3574                        edgeElement = parent.getElement(edgeElementIndex);
3575                        break;
3576                    default:
3577                        throw new AssertionError(direction);
3578                    }
3579                    switch (direction) {
3580                    case -1:
3581                        return edgeElement.getStartOffset();
3582                    case 1:
3583                        return edgeElement.getEndOffset();
3584                    default:
3585                        // we already caught this case earlier; this is to satisfy 
3586                        // the compiler...
3587                        return Integer.MIN_VALUE;
3588                    }
3589                }
3590
3591                // getTextRange() not needed; defined in AccessibleEditableText
3592
3593                /**
3594                 * Returns the <code>AccessibleTextSequence</code> at a given 
3595                 * <code>index</code>.
3596                 *
3597                 * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3598                 * <code>SENTENCE</code>, <code>LINE</code> or 
3599                 * <code>ATTRIBUTE_RUN</code> to retrieve
3600                 * @param index an index within the text
3601                 * @return an <code>AccessibleTextSequence</code> specifying the text if
3602                 * <code>part</code> and <code>index</code> are valid.  Otherwise,
3603                 * <code>null</code> is returned
3604                 *
3605                 * @see javax.accessibility.AccessibleText#CHARACTER
3606                 * @see javax.accessibility.AccessibleText#WORD
3607                 * @see javax.accessibility.AccessibleText#SENTENCE
3608                 * @see javax.accessibility.AccessibleExtendedText#LINE
3609                 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3610                 *
3611                 * @since 1.6
3612                 */
3613                public AccessibleTextSequence getTextSequenceAt(int part,
3614                        int index) {
3615                    return getSequenceAtIndex(part, index, 0);
3616                }
3617
3618                /**
3619                 * Returns the <code>AccessibleTextSequence</code> after a given 
3620                 * <code>index</code>.
3621                 *
3622                 * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3623                 * <code>SENTENCE</code>, <code>LINE</code> or 
3624                 * <code>ATTRIBUTE_RUN</code> to retrieve
3625                 * @param index an index within the text
3626                 * @return an <code>AccessibleTextSequence</code> specifying the text
3627                 * if <code>part</code> and <code>index</code> are valid.  Otherwise,
3628                 * <code>null</code> is returned
3629                 *
3630                 * @see javax.accessibility.AccessibleText#CHARACTER
3631                 * @see javax.accessibility.AccessibleText#WORD
3632                 * @see javax.accessibility.AccessibleText#SENTENCE
3633                 * @see javax.accessibility.AccessibleExtendedText#LINE
3634                 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3635                 *
3636                 * @since 1.6
3637                 */
3638                public AccessibleTextSequence getTextSequenceAfter(int part,
3639                        int index) {
3640                    return getSequenceAtIndex(part, index, 1);
3641                }
3642
3643                /**
3644                 * Returns the <code>AccessibleTextSequence</code> before a given 
3645                 * <code>index</code>.
3646                 *
3647                 * @param part the <code>CHARACTER</code>, <code>WORD</code>,
3648                 * <code>SENTENCE</code>, <code>LINE</code> or 
3649                 * <code>ATTRIBUTE_RUN</code> to retrieve
3650                 * @param index an index within the text
3651                 * @return an <code>AccessibleTextSequence</code> specifying the text 
3652                 * if <code>part</code> and <code>index</code> are valid.  Otherwise,
3653                 * <code>null</code> is returned
3654                 *
3655                 * @see javax.accessibility.AccessibleText#CHARACTER
3656                 * @see javax.accessibility.AccessibleText#WORD
3657                 * @see javax.accessibility.AccessibleText#SENTENCE
3658                 * @see javax.accessibility.AccessibleExtendedText#LINE
3659                 * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN
3660                 *
3661                 * @since 1.6
3662                 */
3663                public AccessibleTextSequence getTextSequenceBefore(int part,
3664                        int index) {
3665                    return getSequenceAtIndex(part, index, -1);
3666                }
3667
3668                /**
3669                 * Returns the <code>Rectangle</code> enclosing the text between
3670                 * two indicies.
3671                 *
3672                 * @param startIndex the start index in the text
3673                 * @param endIndex the end index in the text
3674                 * @return the bounding rectangle of the text if the indices are valid.
3675                 * Otherwise, <code>null</code> is returned
3676                 *
3677                 * @since 1.6
3678                 */
3679                public Rectangle getTextBounds(int startIndex, int endIndex) {
3680                    if (startIndex < 0 || startIndex > model.getLength() - 1
3681                            || endIndex < 0 || endIndex > model.getLength() - 1
3682                            || startIndex > endIndex) {
3683                        return null;
3684                    }
3685                    TextUI ui = getUI();
3686                    if (ui == null) {
3687                        return null;
3688                    }
3689                    Rectangle rect = null;
3690                    Rectangle alloc = getRootEditorRect();
3691                    if (alloc == null) {
3692                        return null;
3693                    }
3694                    if (model instanceof  AbstractDocument) {
3695                        ((AbstractDocument) model).readLock();
3696                    }
3697                    try {
3698                        View rootView = ui.getRootView(JTextComponent.this );
3699                        if (rootView != null) {
3700                            Shape bounds = rootView.modelToView(startIndex,
3701                                    Position.Bias.Forward, endIndex,
3702                                    Position.Bias.Backward, alloc);
3703
3704                            rect = (bounds instanceof  Rectangle) ? (Rectangle) bounds
3705                                    : bounds.getBounds();
3706
3707                        }
3708                    } catch (BadLocationException e) {
3709                    } finally {
3710                        if (model instanceof  AbstractDocument) {
3711                            ((AbstractDocument) model).readUnlock();
3712                        }
3713                    }
3714                    return rect;
3715                }
3716
3717                // ----- end AccessibleExtendedText methods
3718
3719                // --- interface AccessibleAction methods ------------------------
3720
3721                public AccessibleAction getAccessibleAction() {
3722                    return this ;
3723                }
3724
3725                /**
3726                 * Returns the number of accessible actions available in this object
3727                 * If there are more than one, the first one is considered the 
3728                 * "default" action of the object.
3729                 *
3730                 * @return the zero-based number of Actions in this object
3731                 * @since 1.4
3732                 */
3733                public int getAccessibleActionCount() {
3734                    Action[] actions = JTextComponent.this .getActions();
3735                    return actions.length;
3736                }
3737
3738                /**
3739                 * Returns a description of the specified action of the object.
3740                 *
3741                 * @param i zero-based index of the actions
3742                 * @return a String description of the action
3743                 * @see #getAccessibleActionCount
3744                 * @since 1.4
3745                 */
3746                public String getAccessibleActionDescription(int i) {
3747                    Action[] actions = JTextComponent.this .getActions();
3748                    if (i < 0 || i >= actions.length) {
3749                        return null;
3750                    }
3751                    return (String) actions[i].getValue(Action.NAME);
3752                }
3753
3754                /**
3755                 * Performs the specified Action on the object
3756                 *
3757                 * @param i zero-based index of actions
3758                 * @return true if the action was performed; otherwise false.
3759                 * @see #getAccessibleActionCount
3760                 * @since 1.4
3761                 */
3762                public boolean doAccessibleAction(int i) {
3763                    Action[] actions = JTextComponent.this .getActions();
3764                    if (i < 0 || i >= actions.length) {
3765                        return false;
3766                    }
3767                    ActionEvent ae = new ActionEvent(JTextComponent.this ,
3768                            ActionEvent.ACTION_PERFORMED, null, EventQueue
3769                                    .getMostRecentEventTime(),
3770                            getCurrentEventModifiers());
3771                    actions[i].actionPerformed(ae);
3772                    return true;
3773                }
3774
3775                // ----- end AccessibleAction methods
3776
3777            }
3778
3779            // --- serialization ---------------------------------------------
3780
3781            private void readObject(ObjectInputStream s) throws IOException,
3782                    ClassNotFoundException {
3783                s.defaultReadObject();
3784                caretEvent = new MutableCaretEvent(this );
3785                addMouseListener(caretEvent);
3786                addFocusListener(caretEvent);
3787            }
3788
3789            // --- member variables ----------------------------------
3790
3791            /**
3792             * The document model.
3793             */
3794            private Document model;
3795
3796            /**
3797             * The caret used to display the insert position
3798             * and navigate throughout the document. 
3799             *
3800             * PENDING(prinz)  
3801             * This should be serializable, default installed
3802             * by UI.
3803             */
3804            private transient Caret caret;
3805
3806            /**
3807             * Object responsible for restricting the cursor navigation.
3808             */
3809            private NavigationFilter navigationFilter;
3810
3811            /**
3812             * The object responsible for managing highlights.
3813             *
3814             * PENDING(prinz)  
3815             * This should be serializable, default installed
3816             * by UI.
3817             */
3818            private transient Highlighter highlighter;
3819
3820            /**
3821             * The current key bindings in effect.
3822             *
3823             * PENDING(prinz)  
3824             * This should be serializable, default installed
3825             * by UI.
3826             */
3827            private transient Keymap keymap;
3828
3829            private transient MutableCaretEvent caretEvent;
3830            private Color caretColor;
3831            private Color selectionColor;
3832            private Color selectedTextColor;
3833            private Color disabledTextColor;
3834            private boolean editable;
3835            private Insets margin;
3836            private char focusAccelerator;
3837            private boolean dragEnabled;
3838
3839            /**
3840             * The drop mode for this component.
3841             */
3842            private DropMode dropMode = DropMode.USE_SELECTION;
3843
3844            /**
3845             * The drop location.
3846             */
3847            private transient DropLocation dropLocation;
3848
3849            /**
3850             * Represents a drop location for <code>JTextComponent</code>s.
3851             *
3852             * @see #getDropLocation
3853             * @since 1.6
3854             */
3855            public static final class DropLocation extends
3856                    TransferHandler.DropLocation {
3857                private final int index;
3858                private final Position.Bias bias;
3859
3860                private DropLocation(Point p, int index, Position.Bias bias) {
3861                    super (p);
3862                    this .index = index;
3863                    this .bias = bias;
3864                }
3865
3866                /**
3867                 * Returns the index where dropped data should be inserted into the
3868                 * associated component. This index represents a position between
3869                 * characters, as would be interpreted by a caret.
3870                 *
3871                 * @return the drop index
3872                 */
3873                public int getIndex() {
3874                    return index;
3875                }
3876
3877                /**
3878                 * Returns the bias for the drop index.
3879                 *
3880                 * @return the drop bias
3881                 */
3882                public Position.Bias getBias() {
3883                    return bias;
3884                }
3885
3886                /**
3887                 * Returns a string representation of this drop location.
3888                 * This method is intended to be used for debugging purposes,
3889                 * and the content and format of the returned string may vary
3890                 * between implementations.
3891                 *
3892                 * @return a string representation of this drop location
3893                 */
3894                public String toString() {
3895                    return getClass().getName() + "[dropPoint="
3896                            + getDropPoint() + "," + "index=" + index + ","
3897                            + "bias=" + bias + "]";
3898                }
3899            }
3900
3901            /**
3902             * TransferHandler used if one hasn't been supplied by the UI.
3903             */
3904            private static DefaultTransferHandler defaultTransferHandler;
3905
3906            /**
3907             * Maps from class name to Boolean indicating if
3908             * <code>processInputMethodEvent</code> has been overriden.
3909             */
3910            private static Map overrideMap;
3911
3912            /**
3913             * Returns a string representation of this <code>JTextComponent</code>.
3914             * This method is intended to be used only for debugging purposes, and the 
3915             * content and format of the returned string may vary between      
3916             * implementations. The returned string may be empty but may not 
3917             * be <code>null</code>.
3918             * <P>
3919             * Overriding <code>paramString</code> to provide information about the
3920             * specific new aspects of the JFC components.
3921             * 
3922             * @return  a string representation of this <code>JTextComponent</code>
3923             */
3924            protected String paramString() {
3925                String editableString = (editable ? "true" : "false");
3926                String caretColorString = (caretColor != null ? caretColor
3927                        .toString() : "");
3928                String selectionColorString = (selectionColor != null ? selectionColor
3929                        .toString()
3930                        : "");
3931                String selectedTextColorString = (selectedTextColor != null ? selectedTextColor
3932                        .toString()
3933                        : "");
3934                String disabledTextColorString = (disabledTextColor != null ? disabledTextColor
3935                        .toString()
3936                        : "");
3937                String marginString = (margin != null ? margin.toString() : "");
3938
3939                return super .paramString() + ",caretColor=" + caretColorString
3940                        + ",disabledTextColor=" + disabledTextColorString
3941                        + ",editable=" + editableString + ",margin="
3942                        + marginString + ",selectedTextColor="
3943                        + selectedTextColorString + ",selectionColor="
3944                        + selectionColorString;
3945            }
3946
3947            /**
3948             * A Simple TransferHandler that exports the data as a String, and
3949             * imports the data from the String clipboard.  This is only used
3950             * if the UI hasn't supplied one, which would only happen if someone
3951             * hasn't subclassed Basic.
3952             */
3953            static class DefaultTransferHandler extends TransferHandler
3954                    implements  UIResource {
3955                public void exportToClipboard(JComponent comp,
3956                        Clipboard clipboard, int action)
3957                        throws IllegalStateException {
3958                    if (comp instanceof  JTextComponent) {
3959                        JTextComponent text = (JTextComponent) comp;
3960                        int p0 = text.getSelectionStart();
3961                        int p1 = text.getSelectionEnd();
3962                        if (p0 != p1) {
3963                            try {
3964                                Document doc = text.getDocument();
3965                                String srcData = doc.getText(p0, p1 - p0);
3966                                StringSelection contents = new StringSelection(
3967                                        srcData);
3968
3969                                // this may throw an IllegalStateException,
3970                                // but it will be caught and handled in the
3971                                // action that invoked this method
3972                                clipboard.setContents(contents, null);
3973
3974                                if (action == TransferHandler.MOVE) {
3975                                    doc.remove(p0, p1 - p0);
3976                                }
3977                            } catch (BadLocationException ble) {
3978                            }
3979                        }
3980                    }
3981                }
3982
3983                public boolean importData(JComponent comp, Transferable t) {
3984                    if (comp instanceof  JTextComponent) {
3985                        DataFlavor flavor = getFlavor(t
3986                                .getTransferDataFlavors());
3987
3988                        if (flavor != null) {
3989                            InputContext ic = comp.getInputContext();
3990                            if (ic != null) {
3991                                ic.endComposition();
3992                            }
3993                            try {
3994                                String data = (String) t
3995                                        .getTransferData(flavor);
3996
3997                                ((JTextComponent) comp).replaceSelection(data);
3998                                return true;
3999                            } catch (UnsupportedFlavorException ufe) {
4000                            } catch (IOException ioe) {
4001                            }
4002                        }
4003                    }
4004                    return false;
4005                }
4006
4007                public boolean canImport(JComponent comp,
4008                        DataFlavor[] transferFlavors) {
4009                    JTextComponent c = (JTextComponent) comp;
4010                    if (!(c.isEditable() && c.isEnabled())) {
4011                        return false;
4012                    }
4013                    return (getFlavor(transferFlavors) != null);
4014                }
4015
4016                public int getSourceActions(JComponent c) {
4017                    return NONE;
4018                }
4019
4020                private DataFlavor getFlavor(DataFlavor[] flavors) {
4021                    if (flavors != null) {
4022                        for (int counter = 0; counter < flavors.length; counter++) {
4023                            if (flavors[counter]
4024                                    .equals(DataFlavor.stringFlavor)) {
4025                                return flavors[counter];
4026                            }
4027                        }
4028                    }
4029                    return null;
4030                }
4031            }
4032
4033            /**
4034             * Returns the JTextComponent that most recently had focus. The returned
4035             * value may currently have focus.
4036             */
4037            static final JTextComponent getFocusedComponent() {
4038                return (JTextComponent) AppContext.getAppContext().get(
4039                        FOCUSED_COMPONENT);
4040            }
4041
4042            private int getCurrentEventModifiers() {
4043                int modifiers = 0;
4044                AWTEvent currentEvent = EventQueue.getCurrentEvent();
4045                if (currentEvent instanceof  InputEvent) {
4046                    modifiers = ((InputEvent) currentEvent).getModifiers();
4047                } else if (currentEvent instanceof  ActionEvent) {
4048                    modifiers = ((ActionEvent) currentEvent).getModifiers();
4049                }
4050                return modifiers;
4051            }
4052
4053            private static final Object KEYMAP_TABLE = new StringBuilder(
4054                    "JTextComponent_KeymapTable");
4055            private JTextComponent editor;
4056            //
4057            // member variables used for on-the-spot input method 
4058            // editing style support
4059            // 
4060            private transient InputMethodRequests inputMethodRequestsHandler;
4061            private SimpleAttributeSet composedTextAttribute;
4062            private String composedTextContent;
4063            private Position composedTextStart;
4064            private Position composedTextEnd;
4065            private Position latestCommittedTextStart;
4066            private Position latestCommittedTextEnd;
4067            private ComposedTextCaret composedTextCaret;
4068            private transient Caret originalCaret;
4069            /**
4070             * Set to true after the check for the override of processInputMethodEvent
4071             * has been checked.
4072             */
4073            private boolean checkedInputOverride;
4074            private boolean needToSendKeyTypedEvent;
4075
4076            static class DefaultKeymap implements  Keymap {
4077
4078                DefaultKeymap(String nm, Keymap parent) {
4079                    this .nm = nm;
4080                    this .parent = parent;
4081                    bindings = new Hashtable();
4082                }
4083
4084                /**
4085                 * Fetch the default action to fire if a 
4086                 * key is typed (ie a KEY_TYPED KeyEvent is received)
4087                 * and there is no binding for it.  Typically this
4088                 * would be some action that inserts text so that 
4089                 * the keymap doesn't require an action for each 
4090                 * possible key.
4091                 */
4092                public Action getDefaultAction() {
4093                    if (defaultAction != null) {
4094                        return defaultAction;
4095                    }
4096                    return (parent != null) ? parent.getDefaultAction() : null;
4097                }
4098
4099                /**
4100                 * Set the default action to fire if a key is typed.
4101                 */
4102                public void setDefaultAction(Action a) {
4103                    defaultAction = a;
4104                }
4105
4106                public String getName() {
4107                    return nm;
4108                }
4109
4110                public Action getAction(KeyStroke key) {
4111                    Action a = (Action) bindings.get(key);
4112                    if ((a == null) && (parent != null)) {
4113                        a = parent.getAction(key);
4114                    }
4115                    return a;
4116                }
4117
4118                public KeyStroke[] getBoundKeyStrokes() {
4119                    KeyStroke[] keys = new KeyStroke[bindings.size()];
4120                    int i = 0;
4121                    for (Enumeration e = bindings.keys(); e.hasMoreElements();) {
4122                        keys[i++] = (KeyStroke) e.nextElement();
4123                    }
4124                    return keys;
4125                }
4126
4127                public Action[] getBoundActions() {
4128                    Action[] actions = new Action[bindings.size()];
4129                    int i = 0;
4130                    for (Enumeration e = bindings.elements(); e
4131                            .hasMoreElements();) {
4132                        actions[i++] = (Action) e.nextElement();
4133                    }
4134                    return actions;
4135                }
4136
4137                public KeyStroke[] getKeyStrokesForAction(Action a) {
4138                    if (a == null) {
4139                        return null;
4140                    }
4141                    KeyStroke[] retValue = null;
4142                    // Determine local bindings first.
4143                    Vector keyStrokes = null;
4144                    for (Enumeration enum_ = bindings.keys(); enum_
4145                            .hasMoreElements();) {
4146                        Object key = enum_.nextElement();
4147                        if (bindings.get(key) == a) {
4148                            if (keyStrokes == null) {
4149                                keyStrokes = new Vector();
4150                            }
4151                            keyStrokes.addElement(key);
4152                        }
4153                    }
4154                    // See if the parent has any.
4155                    if (parent != null) {
4156                        KeyStroke[] pStrokes = parent.getKeyStrokesForAction(a);
4157                        if (pStrokes != null) {
4158                            // Remove any bindings defined in the parent that
4159                            // are locally defined.
4160                            int rCount = 0;
4161                            for (int counter = pStrokes.length - 1; counter >= 0; counter--) {
4162                                if (isLocallyDefined(pStrokes[counter])) {
4163                                    pStrokes[counter] = null;
4164                                    rCount++;
4165                                }
4166                            }
4167                            if (rCount > 0 && rCount < pStrokes.length) {
4168                                if (keyStrokes == null) {
4169                                    keyStrokes = new Vector();
4170                                }
4171                                for (int counter = pStrokes.length - 1; counter >= 0; counter--) {
4172                                    if (pStrokes[counter] != null) {
4173                                        keyStrokes
4174                                                .addElement(pStrokes[counter]);
4175                                    }
4176                                }
4177                            } else if (rCount == 0) {
4178                                if (keyStrokes == null) {
4179                                    retValue = pStrokes;
4180                                } else {
4181                                    retValue = new KeyStroke[keyStrokes.size()
4182                                            + pStrokes.length];
4183                                    keyStrokes.copyInto(retValue);
4184                                    System.arraycopy(pStrokes, 0, retValue,
4185                                            keyStrokes.size(), pStrokes.length);
4186                                    keyStrokes = null;
4187                                }
4188                            }
4189                        }
4190                    }
4191                    if (keyStrokes != null) {
4192                        retValue = new KeyStroke[keyStrokes.size()];
4193                        keyStrokes.copyInto(retValue);
4194                    }
4195                    return retValue;
4196                }
4197
4198                public boolean isLocallyDefined(KeyStroke key) {
4199                    return bindings.containsKey(key);
4200                }
4201
4202                public void addActionForKeyStroke(KeyStroke key, Action a) {
4203                    bindings.put(key, a);
4204                }
4205
4206                public void removeKeyStrokeBinding(KeyStroke key) {
4207                    bindings.remove(key);
4208                }
4209
4210                public void removeBindings() {
4211                    bindings.clear();
4212                }
4213
4214                public Keymap getResolveParent() {
4215                    return parent;
4216                }
4217
4218                public void setResolveParent(Keymap parent) {
4219                    this .parent = parent;
4220                }
4221
4222                /**
4223                 * String representation of the keymap... potentially 
4224                 * a very long string.
4225                 */
4226                public String toString() {
4227                    return "Keymap[" + nm + "]" + bindings;
4228                }
4229
4230                String nm;
4231                Keymap parent;
4232                Hashtable bindings;
4233                Action defaultAction;
4234            }
4235
4236            /**
4237             * KeymapWrapper wraps a Keymap inside an InputMap. For KeymapWrapper
4238             * to be useful it must be used with a KeymapActionMap.
4239             * KeymapWrapper for the most part, is an InputMap with two parents.
4240             * The first parent visited is ALWAYS the Keymap, with the second
4241             * parent being the parent inherited from InputMap. If
4242             * <code>keymap.getAction</code> returns null, implying the Keymap
4243             * does not have a binding for the KeyStroke,
4244             * the parent is then visited. If the Keymap has a binding, the 
4245             * Action is returned, if not and the KeyStroke represents a
4246             * KeyTyped event and the Keymap has a defaultAction,
4247             * <code>DefaultActionKey</code> is returned.
4248             * <p>KeymapActionMap is then able to transate the object passed in
4249             * to either message the Keymap, or message its default implementation.
4250             */
4251            static class KeymapWrapper extends InputMap {
4252                static final Object DefaultActionKey = new Object();
4253
4254                private Keymap keymap;
4255
4256                KeymapWrapper(Keymap keymap) {
4257                    this .keymap = keymap;
4258                }
4259
4260                public KeyStroke[] keys() {
4261                    KeyStroke[] sKeys = super .keys();
4262                    KeyStroke[] keymapKeys = keymap.getBoundKeyStrokes();
4263                    int sCount = (sKeys == null) ? 0 : sKeys.length;
4264                    int keymapCount = (keymapKeys == null) ? 0
4265                            : keymapKeys.length;
4266                    if (sCount == 0) {
4267                        return keymapKeys;
4268                    }
4269                    if (keymapCount == 0) {
4270                        return sKeys;
4271                    }
4272                    KeyStroke[] retValue = new KeyStroke[sCount + keymapCount];
4273                    // There may be some duplication here...
4274                    System.arraycopy(sKeys, 0, retValue, 0, sCount);
4275                    System.arraycopy(keymapKeys, 0, retValue, sCount,
4276                            keymapCount);
4277                    return retValue;
4278                }
4279
4280                public int size() {
4281                    // There may be some duplication here...
4282                    KeyStroke[] keymapStrokes = keymap.getBoundKeyStrokes();
4283                    int keymapCount = (keymapStrokes == null) ? 0
4284                            : keymapStrokes.length;
4285                    return super .size() + keymapCount;
4286                }
4287
4288                public Object get(KeyStroke keyStroke) {
4289                    Object retValue = keymap.getAction(keyStroke);
4290                    if (retValue == null) {
4291                        retValue = super .get(keyStroke);
4292                        if (retValue == null
4293                                && keyStroke.getKeyChar() != KeyEvent.CHAR_UNDEFINED
4294                                && keymap.getDefaultAction() != null) {
4295                            // Implies this is a KeyTyped event, use the default
4296                            // action.
4297                            retValue = DefaultActionKey;
4298                        }
4299                    }
4300                    return retValue;
4301                }
4302            }
4303
4304            /**
4305             * Wraps a Keymap inside an ActionMap. This is used with
4306             * a KeymapWrapper. If <code>get</code> is passed in
4307             * <code>KeymapWrapper.DefaultActionKey</code>, the default action is
4308             * returned, otherwise if the key is an Action, it is returned.
4309             */
4310            static class KeymapActionMap extends ActionMap {
4311                private Keymap keymap;
4312
4313                KeymapActionMap(Keymap keymap) {
4314                    this .keymap = keymap;
4315                }
4316
4317                public Object[] keys() {
4318                    Object[] sKeys = super .keys();
4319                    Object[] keymapKeys = keymap.getBoundActions();
4320                    int sCount = (sKeys == null) ? 0 : sKeys.length;
4321                    int keymapCount = (keymapKeys == null) ? 0
4322                            : keymapKeys.length;
4323                    boolean hasDefault = (keymap.getDefaultAction() != null);
4324                    if (hasDefault) {
4325                        keymapCount++;
4326                    }
4327                    if (sCount == 0) {
4328                        if (hasDefault) {
4329                            Object[] retValue = new Object[keymapCount];
4330                            if (keymapCount > 1) {
4331                                System.arraycopy(keymapKeys, 0, retValue, 0,
4332                                        keymapCount - 1);
4333                            }
4334                            retValue[keymapCount - 1] = KeymapWrapper.DefaultActionKey;
4335                            return retValue;
4336                        }
4337                        return keymapKeys;
4338                    }
4339                    if (keymapCount == 0) {
4340                        return sKeys;
4341                    }
4342                    Object[] retValue = new Object[sCount + keymapCount];
4343                    // There may be some duplication here...
4344                    System.arraycopy(sKeys, 0, retValue, 0, sCount);
4345                    if (hasDefault) {
4346                        if (keymapCount > 1) {
4347                            System.arraycopy(keymapKeys, 0, retValue, sCount,
4348                                    keymapCount - 1);
4349                        }
4350                        retValue[sCount + keymapCount - 1] = KeymapWrapper.DefaultActionKey;
4351                    } else {
4352                        System.arraycopy(keymapKeys, 0, retValue, sCount,
4353                                keymapCount);
4354                    }
4355                    return retValue;
4356                }
4357
4358                public int size() {
4359                    // There may be some duplication here...
4360                    Object[] actions = keymap.getBoundActions();
4361                    int keymapCount = (actions == null) ? 0 : actions.length;
4362                    if (keymap.getDefaultAction() != null) {
4363                        keymapCount++;
4364                    }
4365                    return super .size() + keymapCount;
4366                }
4367
4368                public Action get(Object key) {
4369                    Action retValue = super .get(key);
4370                    if (retValue == null) {
4371                        // Try the Keymap.
4372                        if (key == KeymapWrapper.DefaultActionKey) {
4373                            retValue = keymap.getDefaultAction();
4374                        } else if (key instanceof  Action) {
4375                            // This is a little iffy, technically an Action is
4376                            // a valid Key. We're assuming the Action came from
4377                            // the InputMap though.
4378                            retValue = (Action) key;
4379                        }
4380                    }
4381                    return retValue;
4382                }
4383            }
4384
4385            private static final Object FOCUSED_COMPONENT = new StringBuilder(
4386                    "JTextComponent_FocusedComponent");
4387
4388            /**
4389             * The default keymap that will be shared by all
4390             * <code>JTextComponent</code> instances unless they
4391             * have had a different keymap set. 
4392             */
4393            public static final String DEFAULT_KEYMAP = "default";
4394
4395            /**
4396             * Event to use when firing a notification of change to caret 
4397             * position.  This is mutable so that the event can be reused
4398             * since caret events can be fairly high in bandwidth.
4399             */
4400            static class MutableCaretEvent extends CaretEvent implements 
4401                    ChangeListener, FocusListener, MouseListener {
4402
4403                MutableCaretEvent(JTextComponent c) {
4404                    super (c);
4405                }
4406
4407                final void fire() {
4408                    JTextComponent c = (JTextComponent) getSource();
4409                    if (c != null) {
4410                        Caret caret = c.getCaret();
4411                        dot = caret.getDot();
4412                        mark = caret.getMark();
4413                        c.fireCaretUpdate(this );
4414                    }
4415                }
4416
4417                public final String toString() {
4418                    return "dot=" + dot + "," + "mark=" + mark;
4419                }
4420
4421                // --- CaretEvent methods -----------------------
4422
4423                public final int getDot() {
4424                    return dot;
4425                }
4426
4427                public final int getMark() {
4428                    return mark;
4429                }
4430
4431                // --- ChangeListener methods -------------------
4432
4433                public final void stateChanged(ChangeEvent e) {
4434                    if (!dragActive) {
4435                        fire();
4436                    }
4437                }
4438
4439                // --- FocusListener methods -----------------------------------
4440                public void focusGained(FocusEvent fe) {
4441                    AppContext.getAppContext().put(FOCUSED_COMPONENT,
4442                            fe.getSource());
4443                }
4444
4445                public void focusLost(FocusEvent fe) {
4446                }
4447
4448                // --- MouseListener methods -----------------------------------
4449
4450                /**
4451                 * Requests focus on the associated
4452                 * text component, and try to set the cursor position.
4453                 *
4454                 * @param e the mouse event
4455                 * @see MouseListener#mousePressed
4456                 */
4457                public final void mousePressed(MouseEvent e) {
4458                    dragActive = true;
4459                }
4460
4461                /**
4462                 * Called when the mouse is released.
4463                 *
4464                 * @param e the mouse event
4465                 * @see MouseListener#mouseReleased
4466                 */
4467                public final void mouseReleased(MouseEvent e) {
4468                    dragActive = false;
4469                    fire();
4470                }
4471
4472                public final void mouseClicked(MouseEvent e) {
4473                }
4474
4475                public final void mouseEntered(MouseEvent e) {
4476                }
4477
4478                public final void mouseExited(MouseEvent e) {
4479                }
4480
4481                private boolean dragActive;
4482                private int dot;
4483                private int mark;
4484            }
4485
4486            //
4487            // Process any input method events that the component itself 
4488            // recognizes. The default on-the-spot handling for input method 
4489            // composed(uncommitted) text is done here after all input 
4490            // method listeners get called for stealing the events.
4491            //
4492            protected void processInputMethodEvent(InputMethodEvent e) {
4493                // let listeners handle the events
4494                super .processInputMethodEvent(e);
4495
4496                if (!e.isConsumed()) {
4497                    if (!isEditable()) {
4498                        return;
4499                    } else {
4500                        switch (e.getID()) {
4501                        case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED:
4502                            replaceInputMethodText(e);
4503
4504                            // fall through
4505
4506                        case InputMethodEvent.CARET_POSITION_CHANGED:
4507                            setInputMethodCaretPosition(e);
4508                            break;
4509                        }
4510                    }
4511
4512                    e.consume();
4513                }
4514            }
4515
4516            //
4517            // Overrides this method to become an active input method client.
4518            //
4519            public InputMethodRequests getInputMethodRequests() {
4520                if (inputMethodRequestsHandler == null) {
4521                    inputMethodRequestsHandler = (InputMethodRequests) new InputMethodRequestsHandler();
4522                    Document doc = getDocument();
4523                    if (doc != null) {
4524                        doc
4525                                .addDocumentListener((DocumentListener) inputMethodRequestsHandler);
4526                    }
4527                }
4528
4529                return inputMethodRequestsHandler;
4530            }
4531
4532            //
4533            // Overrides this method to watch the listener installed.
4534            //
4535            public void addInputMethodListener(InputMethodListener l) {
4536                super .addInputMethodListener(l);
4537                if (l != null) {
4538                    needToSendKeyTypedEvent = false;
4539                    checkedInputOverride = true;
4540                }
4541            }
4542
4543            //
4544            // Default implementation of the InputMethodRequests interface.
4545            //
4546            class InputMethodRequestsHandler implements  InputMethodRequests,
4547                    DocumentListener {
4548
4549                // --- InputMethodRequests methods ---
4550
4551                public AttributedCharacterIterator cancelLatestCommittedText(
4552                        Attribute[] attributes) {
4553                    Document doc = getDocument();
4554                    if ((doc != null)
4555                            && (latestCommittedTextStart != null)
4556                            && (!latestCommittedTextStart
4557                                    .equals(latestCommittedTextEnd))) {
4558                        try {
4559                            int startIndex = latestCommittedTextStart
4560                                    .getOffset();
4561                            int endIndex = latestCommittedTextEnd.getOffset();
4562                            String latestCommittedText = doc.getText(
4563                                    startIndex, endIndex - startIndex);
4564                            doc.remove(startIndex, endIndex - startIndex);
4565                            return new AttributedString(latestCommittedText)
4566                                    .getIterator();
4567                        } catch (BadLocationException ble) {
4568                        }
4569                    }
4570                    return null;
4571                }
4572
4573                public AttributedCharacterIterator getCommittedText(
4574                        int beginIndex, 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
4587                                        - beginIndex);
4588                            } else {
4589                                int firstPartLength = composedStartIndex
4590                                        - beginIndex;
4591                                committed = getText(beginIndex, firstPartLength)
4592                                        + getText(composedEndIndex, endIndex
4593                                                - beginIndex - firstPartLength);
4594                            }
4595                        } else {
4596                            committed = getText(beginIndex
4597                                    + (composedEndIndex - composedStartIndex),
4598                                    endIndex - beginIndex);
4599                        }
4600                    } catch (BadLocationException ble) {
4601                        throw new IllegalArgumentException("Invalid range");
4602                    }
4603                    return new AttributedString(committed).getIterator();
4604                }
4605
4606                public int getCommittedTextLength() {
4607                    Document doc = getDocument();
4608                    int length = 0;
4609                    if (doc != null) {
4610                        length = doc.getLength();
4611                        if (composedTextContent != null) {
4612                            if (composedTextEnd == null
4613                                    || composedTextStart == null) {
4614                                /* 
4615                                 * fix for : 6355666
4616                                 * this is the case when this method is invoked
4617                                 * from DocumentListener. At this point 
4618                                 * composedTextEnd and composedTextStart are
4619                                 * not defined yet.
4620                                 */
4621                                length -= composedTextContent.length();
4622                            } else {
4623                                length -= composedTextEnd.getOffset()
4624                                        - composedTextStart.getOffset();
4625                            }
4626                        }
4627                    }
4628                    return length;
4629                }
4630
4631                public int getInsertPositionOffset() {
4632                    int composedStartIndex = 0;
4633                    int composedEndIndex = 0;
4634                    if (composedTextExists()) {
4635                        composedStartIndex = composedTextStart.getOffset();
4636                        composedEndIndex = composedTextEnd.getOffset();
4637                    }
4638                    int caretIndex = getCaretPosition();
4639
4640                    if (caretIndex < composedStartIndex) {
4641                        return caretIndex;
4642                    } else if (caretIndex < composedEndIndex) {
4643                        return composedStartIndex;
4644                    } else {
4645                        return caretIndex
4646                                - (composedEndIndex - composedStartIndex);
4647                    }
4648                }
4649
4650                public TextHitInfo getLocationOffset(int x, int y) {
4651                    if (composedTextAttribute == null) {
4652                        return null;
4653                    } else {
4654                        Point p = getLocationOnScreen();
4655                        p.x = x - p.x;
4656                        p.y = y - p.y;
4657                        int pos = viewToModel(p);
4658                        if ((pos >= composedTextStart.getOffset())
4659                                && (pos <= composedTextEnd.getOffset())) {
4660                            return TextHitInfo.leading(pos
4661                                    - composedTextStart.getOffset());
4662                        } else {
4663                            return null;
4664                        }
4665                    }
4666                }
4667
4668                public Rectangle getTextLocation(TextHitInfo offset) {
4669                    Rectangle r;
4670
4671                    try {
4672                        r = modelToView(getCaretPosition());
4673                        if (r != null) {
4674                            Point p = getLocationOnScreen();
4675                            r.translate(p.x, p.y);
4676                        }
4677                    } catch (BadLocationException ble) {
4678                        r = null;
4679                    }
4680
4681                    if (r == null)
4682                        r = new Rectangle();
4683
4684                    return r;
4685                }
4686
4687                public AttributedCharacterIterator getSelectedText(
4688                        Attribute[] attributes) {
4689                    String selection = JTextComponent.this .getSelectedText();
4690                    if (selection != null) {
4691                        return new AttributedString(selection).getIterator();
4692                    } else {
4693                        return null;
4694                    }
4695                }
4696
4697                // --- DocumentListener methods ---
4698
4699                public void changedUpdate(DocumentEvent e) {
4700                    latestCommittedTextStart = latestCommittedTextEnd = null;
4701                }
4702
4703                public void insertUpdate(DocumentEvent e) {
4704                    latestCommittedTextStart = latestCommittedTextEnd = null;
4705                }
4706
4707                public void removeUpdate(DocumentEvent e) {
4708                    latestCommittedTextStart = latestCommittedTextEnd = null;
4709                }
4710            }
4711
4712            //
4713            // Replaces the current input method (composed) text according to
4714            // the passed input method event. This method also inserts the 
4715            // committed text into the document. 
4716            //
4717            private void replaceInputMethodText(InputMethodEvent e) {
4718                int commitCount = e.getCommittedCharacterCount();
4719                AttributedCharacterIterator text = e.getText();
4720                int composedTextIndex;
4721
4722                // old composed text deletion
4723                Document doc = getDocument();
4724                if (composedTextExists()) {
4725                    try {
4726                        doc.remove(composedTextStart.getOffset(),
4727                                composedTextEnd.getOffset()
4728                                        - composedTextStart.getOffset());
4729                    } catch (BadLocationException ble) {
4730                    }
4731                    composedTextStart = composedTextEnd = null;
4732                    composedTextAttribute = null;
4733                    composedTextContent = null;
4734                }
4735
4736                if (text != null) {
4737                    text.first();
4738                    int committedTextStartIndex = 0;
4739                    int committedTextEndIndex = 0;
4740
4741                    // committed text insertion
4742                    if (commitCount > 0) {
4743                        // Remember latest committed text start index
4744                        committedTextStartIndex = caret.getDot();
4745
4746                        // Need to generate KeyTyped events for the committed text for components
4747                        // that are not aware they are active input method clients.
4748                        if (shouldSynthensizeKeyEvents()) {
4749                            for (char c = text.current(); commitCount > 0; c = text
4750                                    .next(), commitCount--) {
4751                                KeyEvent ke = new KeyEvent(this ,
4752                                        KeyEvent.KEY_TYPED, EventQueue
4753                                                .getMostRecentEventTime(), 0,
4754                                        KeyEvent.VK_UNDEFINED, c);
4755                                processKeyEvent(ke);
4756                            }
4757                        } else {
4758                            StringBuffer strBuf = new StringBuffer();
4759                            for (char c = text.current(); commitCount > 0; c = text
4760                                    .next(), commitCount--) {
4761                                strBuf.append(c);
4762                            }
4763
4764                            // map it to an ActionEvent
4765                            mapCommittedTextToAction(new String(strBuf));
4766                        }
4767
4768                        // Remember latest committed text end index 
4769                        committedTextEndIndex = caret.getDot();
4770                    }
4771
4772                    // new composed text insertion
4773                    composedTextIndex = text.getIndex();
4774                    if (composedTextIndex < text.getEndIndex()) {
4775                        createComposedTextAttribute(composedTextIndex, text);
4776                        try {
4777                            replaceSelection(null);
4778                            doc.insertString(caret.getDot(),
4779                                    composedTextContent, composedTextAttribute);
4780                            composedTextStart = doc.createPosition(caret
4781                                    .getDot()
4782                                    - composedTextContent.length());
4783                            composedTextEnd = doc
4784                                    .createPosition(caret.getDot());
4785                        } catch (BadLocationException ble) {
4786                            composedTextStart = composedTextEnd = null;
4787                            composedTextAttribute = null;
4788                            composedTextContent = null;
4789                        }
4790                    }
4791
4792                    // Save the latest committed text information
4793                    if (committedTextStartIndex != committedTextEndIndex) {
4794                        try {
4795                            latestCommittedTextStart = doc
4796                                    .createPosition(committedTextStartIndex);
4797                            latestCommittedTextEnd = doc
4798                                    .createPosition(committedTextEndIndex);
4799                        } catch (BadLocationException ble) {
4800                            latestCommittedTextStart = latestCommittedTextEnd = null;
4801                        }
4802                    } else {
4803                        latestCommittedTextStart = latestCommittedTextEnd = null;
4804                    }
4805                }
4806            }
4807
4808            private void createComposedTextAttribute(int composedIndex,
4809                    AttributedCharacterIterator text) {
4810                Document doc = getDocument();
4811                StringBuffer strBuf = new StringBuffer();
4812
4813                // create attributed string with no attributes
4814                for (char c = text.setIndex(composedIndex); c != CharacterIterator.DONE; c = text
4815                        .next()) {
4816                    strBuf.append(c);
4817                }
4818
4819                composedTextContent = new String(strBuf);
4820                composedTextAttribute = new SimpleAttributeSet();
4821                composedTextAttribute.addAttribute(
4822                        StyleConstants.ComposedTextAttribute,
4823                        new AttributedString(text, composedIndex, text
4824                                .getEndIndex()));
4825            }
4826
4827            private boolean saveComposedText(int pos) {
4828                if (composedTextExists()) {
4829                    int start = composedTextStart.getOffset();
4830                    int len = composedTextEnd.getOffset()
4831                            - composedTextStart.getOffset();
4832                    if (pos >= start && pos <= start + len) {
4833                        try {
4834                            getDocument().remove(start, len);
4835                            return true;
4836                        } catch (BadLocationException ble) {
4837                        }
4838                    }
4839                }
4840                return false;
4841            }
4842
4843            private void restoreComposedText() {
4844                Document doc = getDocument();
4845                try {
4846                    doc.insertString(caret.getDot(), composedTextContent,
4847                            composedTextAttribute);
4848                    composedTextStart = doc.createPosition(caret.getDot()
4849                            - composedTextContent.length());
4850                    composedTextEnd = doc.createPosition(caret.getDot());
4851                } catch (BadLocationException ble) {
4852                }
4853            }
4854
4855            //
4856            // Map committed text to an ActionEvent. If the committed text length is 1,
4857            // treat it as a KeyStroke, otherwise or there is no KeyStroke defined, 
4858            // treat it just as a default action.
4859            //
4860            private void mapCommittedTextToAction(String committedText) {
4861                Keymap binding = getKeymap();
4862                if (binding != null) {
4863                    Action a = null;
4864                    if (committedText.length() == 1) {
4865                        KeyStroke k = KeyStroke.getKeyStroke(committedText
4866                                .charAt(0));
4867                        a = binding.getAction(k);
4868                    }
4869
4870                    if (a == null) {
4871                        a = binding.getDefaultAction();
4872                    }
4873
4874                    if (a != null) {
4875                        ActionEvent ae = new ActionEvent(this ,
4876                                ActionEvent.ACTION_PERFORMED, committedText,
4877                                EventQueue.getMostRecentEventTime(),
4878                                getCurrentEventModifiers());
4879                        a.actionPerformed(ae);
4880                    }
4881                }
4882            }
4883
4884            //
4885            // Sets the caret position according to the passed input method
4886            // event. Also, sets/resets composed text caret appropriately.
4887            //
4888            private void setInputMethodCaretPosition(InputMethodEvent e) {
4889                int dot;
4890
4891                if (composedTextExists()) {
4892                    dot = composedTextStart.getOffset();
4893                    if (!(caret instanceof  ComposedTextCaret)) {
4894                        if (composedTextCaret == null) {
4895                            composedTextCaret = new ComposedTextCaret();
4896                        }
4897                        originalCaret = caret;
4898                        // Sets composed text caret
4899                        exchangeCaret(originalCaret, composedTextCaret);
4900                    }
4901
4902                    TextHitInfo caretPos = e.getCaret();
4903                    if (caretPos != null) {
4904                        int index = caretPos.getInsertionIndex();
4905                        dot += index;
4906                        if (index == 0) {
4907                            // Scroll the component if needed so that the composed text
4908                            // becomes visible.
4909                            try {
4910                                Rectangle d = modelToView(dot);
4911                                Rectangle end = modelToView(composedTextEnd
4912                                        .getOffset());
4913                                Rectangle b = getBounds();
4914                                d.x += Math.min(end.x - d.x, b.width);
4915                                scrollRectToVisible(d);
4916                            } catch (BadLocationException ble) {
4917                            }
4918                        }
4919                    }
4920                    caret.setDot(dot);
4921                } else if (caret instanceof  ComposedTextCaret) {
4922                    dot = caret.getDot();
4923                    // Restores original caret
4924                    exchangeCaret(caret, originalCaret);
4925                    caret.setDot(dot);
4926                }
4927            }
4928
4929            private void exchangeCaret(Caret oldCaret, Caret newCaret) {
4930                int blinkRate = oldCaret.getBlinkRate();
4931                setCaret(newCaret);
4932                caret.setBlinkRate(blinkRate);
4933                caret.setVisible(hasFocus());
4934            }
4935
4936            /**
4937             * Returns true if KeyEvents should be synthesized from an InputEvent.
4938             */
4939            private boolean shouldSynthensizeKeyEvents() {
4940                if (!checkedInputOverride) {
4941                    checkedInputOverride = true;
4942                    needToSendKeyTypedEvent = !isProcessInputMethodEventOverridden();
4943                }
4944                return needToSendKeyTypedEvent;
4945            }
4946
4947            //
4948            // Checks whether the client code overrides processInputMethodEvent.  If it is overridden,
4949            // need not to generate KeyTyped events for committed text. If it's not, behave as an 
4950            // passive input method client.
4951            //
4952            private boolean isProcessInputMethodEventOverridden() {
4953                if (overrideMap == null) {
4954                    overrideMap = Collections.synchronizedMap(new HashMap());
4955                }
4956                Boolean retValue = (Boolean) overrideMap.get(getClass()
4957                        .getName());
4958
4959                if (retValue != null) {
4960                    return retValue.booleanValue();
4961                }
4962                Boolean ret = (Boolean) AccessController
4963                        .doPrivileged(new PrivilegedAction() {
4964                            public Object run() {
4965                                return isProcessInputMethodEventOverridden(JTextComponent.this 
4966                                        .getClass());
4967                            }
4968                        });
4969
4970                return ret.booleanValue();
4971            }
4972
4973            //
4974            // Checks whether a composed text in this text component
4975            //
4976            boolean composedTextExists() {
4977                return (composedTextStart != null);
4978            }
4979
4980            //
4981            // Caret implementation for editing the composed text.
4982            //
4983            class ComposedTextCaret extends DefaultCaret implements 
4984                    Serializable {
4985                Color bg;
4986
4987                //	
4988                // Get the background color of the component
4989                //	
4990                public void install(JTextComponent c) {
4991                    super .install(c);
4992
4993                    Document doc = c.getDocument();
4994                    if (doc instanceof  StyledDocument) {
4995                        StyledDocument sDoc = (StyledDocument) doc;
4996                        Element elem = sDoc
4997                                .getCharacterElement(c.composedTextStart
4998                                        .getOffset());
4999                        AttributeSet attr = elem.getAttributes();
5000                        bg = sDoc.getBackground(attr);
5001                    }
5002
5003                    if (bg == null) {
5004                        bg = c.getBackground();
5005                    }
5006                }
5007
5008                //
5009                // Draw caret in XOR mode.
5010                // 
5011                public void paint(Graphics g) {
5012                    if (isVisible()) {
5013                        try {
5014                            Rectangle r = component.modelToView(getDot());
5015                            g.setXORMode(bg);
5016                            g.drawLine(r.x, r.y, r.x, r.y + r.height - 1);
5017                            g.setPaintMode();
5018                        } catch (BadLocationException e) {
5019                            // can't render I guess
5020                            //System.err.println("Can't render cursor");
5021                        }
5022                    }
5023                }
5024
5025                //
5026                // If some area other than the composed text is clicked by mouse,
5027                // issue endComposition() to force commit the composed text.
5028                //
5029                protected void positionCaret(MouseEvent me) {
5030                    JTextComponent host = component;
5031                    Point pt = new Point(me.getX(), me.getY());
5032                    int offset = host.viewToModel(pt);
5033                    int composedStartIndex = host.composedTextStart.getOffset();
5034                    if ((offset < composedStartIndex)
5035                            || (offset > composedTextEnd.getOffset())) {
5036                        try {
5037                            // Issue endComposition
5038                            Position newPos = host.getDocument()
5039                                    .createPosition(offset);
5040                            host.getInputContext().endComposition();
5041
5042                            // Post a caret positioning runnable to assure that the positioning
5043                            // occurs *after* committing the composed text.
5044                            EventQueue.invokeLater(new DoSetCaretPosition(host,
5045                                    newPos));
5046                        } catch (BadLocationException ble) {
5047                            System.err.println(ble);
5048                        }
5049                    } else {
5050                        // Normal processing
5051                        super .positionCaret(me);
5052                    }
5053                }
5054            }
5055
5056            //
5057            // Runnable class for invokeLater() to set caret position later.
5058            //
5059            private class DoSetCaretPosition implements  Runnable {
5060                JTextComponent host;
5061                Position newPos;
5062
5063                DoSetCaretPosition(JTextComponent host, Position newPos) {
5064                    this .host = host;
5065                    this .newPos = newPos;
5066                }
5067
5068                public void run() {
5069                    host.setCaretPosition(newPos.getOffset());
5070                }
5071            }
5072        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.