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