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