KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > text > JTextComponent


1 /*
2  * @(#)JTextComponent.java 1.213 06/04/10
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.text;
8
9 import java.lang.reflect.Method JavaDoc;
10
11 import java.security.AccessController JavaDoc;
12 import java.security.PrivilegedAction JavaDoc;
13
14 import java.util.Collections JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.Hashtable JavaDoc;
17 import java.util.Enumeration JavaDoc;
18 import java.util.Vector JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Map.Entry;
22 import java.util.Set JavaDoc;
23
24 import java.io.*;
25
26 import java.awt.*;
27 import java.awt.event.*;
28 import java.awt.datatransfer.*;
29 import java.awt.im.InputContext JavaDoc;
30 import java.awt.im.InputMethodRequests JavaDoc;
31 import java.awt.font.TextHitInfo JavaDoc;
32 import java.awt.font.TextAttribute JavaDoc;
33
34 import java.text.*;
35 import java.text.AttributedCharacterIterator.Attribute;
36
37 import javax.swing.*;
38 import javax.swing.event.*;
39 import javax.swing.plaf.*;
40
41 import javax.accessibility.*;
42
43 import sun.awt.AppContext;
44
45 /**
46  * <code>JTextComponent</code> is the base class for swing text
47  * components. It tries to be compatible with the
48  * <code>java.awt.TextComponent</code> class
49  * where it can reasonably do so. Also provided are other services
50  * for additional flexibility (beyond the pluggable UI and bean
51  * support).
52  * You can find information on how to use the functionality
53  * this class provides in
54  * <a HREF="http://java.sun.com/docs/books/tutorial/uiswing/components/generaltext.html">General Rules for Using Text Components</a>,
55  * a section in <em>The Java Tutorial.</em>
56  *
57  * <p>
58  * <dl>
59  * <dt><b><font size=+1>Caret Changes</font></b>
60  * <dd>
61  * The caret is a pluggable object in swing text components.
62  * Notification of changes to the caret position and the selection
63  * are sent to implementations of the <code>CaretListener</code>
64  * interface that have been registered with the text component.
65  * The UI will install a default caret unless a customized caret
66  * has been set. <br>
67  * By default the caret tracks all the document changes
68  * performed on the Event Dispatching Thread and updates it's position
69  * accordingly if an insertion occurs before or at the caret position
70  * or a removal occurs before the caret position. <code>DefaultCaret</code>
71  * tries to make itself visible which may lead to scrolling
72  * of a text component within <code>JScrollPane</code>. The default caret
73  * behavior can be changed by the {@link DefaultCaret#setUpdatePolicy} method.
74  * <br>
75  * <b>Note</b>: Non-editable text components also have a caret though
76  * it may not be painted.
77  *
78  * <p>
79  * <dt><b><font size=+1>Commands</font></b>
80  * <dd>
81  * Text components provide a number of commands that can be used
82  * to manipulate the component. This is essentially the way that
83  * the component expresses its capabilities. These are expressed
84  * in terms of the swing <code>Action</code> interface,
85  * using the <code>TextAction</code> implementation.
86  * The set of commands supported by the text component can be
87  * found with the {@link #getActions} method. These actions
88  * can be bound to key events, fired from buttons, etc.
89  *
90  * <p>
91  * <dt><b><font size=+1>Text Input</font></b>
92  * <dd>
93  * The text components support flexible and internationalized text input, using
94  * keymaps and the input method framework, while maintaining compatibility with
95  * the AWT listener model.
96  * <p>
97  * A {@link javax.swing.text.Keymap} lets an application bind key
98  * strokes to actions.
99  * In order to allow keymaps to be shared across multiple text components, they
100  * can use actions that extend <code>TextAction</code>.
101  * <code>TextAction</code> can determine which <code>JTextComponent</code>
102  * most recently has or had focus and therefore is the subject of
103  * the action (In the case that the <code>ActionEvent</code>
104  * sent to the action doesn't contain the target text component as its source).
105  * <p>
106  * The <a HREF="../../../../guide/imf/spec.html">input method framework</a>
107  * lets text components interact with input methods, separate software
108  * components that preprocess events to let users enter thousands of
109  * different characters using keyboards with far fewer keys.
110  * <code>JTextComponent</code> is an <em>active client</em> of
111  * the framework, so it implements the preferred user interface for interacting
112  * with input methods. As a consequence, some key events do not reach the text
113  * component because they are handled by an input method, and some text input
114  * reaches the text component as committed text within an {@link
115  * java.awt.event.InputMethodEvent} instead of as a key event.
116  * The complete text input is the combination of the characters in
117  * <code>keyTyped</code> key events and committed text in input method events.
118  * <p>
119  * The AWT listener model lets applications attach event listeners to
120  * components in order to bind events to actions. Swing encourages the
121  * use of keymaps instead of listeners, but maintains compatibility
122  * with listeners by giving the listeners a chance to steal an event
123  * by consuming it.
124  * <p>
125  * Keyboard event and input method events are handled in the following stages,
126  * with each stage capable of consuming the event:
127  *
128  * <table border=1 summary="Stages of keyboard and input method event handling">
129  * <tr>
130  * <th id="stage"><p align="left">Stage</p></th>
131  * <th id="ke"><p align="left">KeyEvent</p></th>
132  * <th id="ime"><p align="left">InputMethodEvent</p></th></tr>
133  * <tr><td headers="stage">1. </td>
134  * <td headers="ke">input methods </td>
135  * <td headers="ime">(generated here)</td></tr>
136  * <tr><td headers="stage">2. </td>
137  * <td headers="ke">focus manager </td>
138  * <td headers="ime"></td>
139  * </tr>
140  * <tr>
141  * <td headers="stage">3. </td>
142  * <td headers="ke">registered key listeners</td>
143  * <td headers="ime">registered input method listeners</tr>
144  * <tr>
145  * <td headers="stage">4. </td>
146  * <td headers="ke"></td>
147  * <td headers="ime">input method handling in JTextComponent</tr>
148  * <tr>
149  * <td headers="stage">5. </td><td headers="ke ime" colspan=2>keymap handling using the current keymap</td></tr>
150  * <tr><td headers="stage">6. </td><td headers="ke">keyboard handling in JComponent (e.g. accelerators, component navigation, etc.)</td>
151  * <td headers="ime"></td></tr>
152  * </table>
153  *
154  * <p>
155  * To maintain compatibility with applications that listen to key
156  * events but are not aware of input method events, the input
157  * method handling in stage 4 provides a compatibility mode for
158  * components that do not process input method events. For these
159  * components, the committed text is converted to keyTyped key events
160  * and processed in the key event pipeline starting at stage 3
161  * instead of in the input method event pipeline.
162  * <p>
163  * By default the component will create a keymap (named <b>DEFAULT_KEYMAP</b>)
164  * that is shared by all JTextComponent instances as the default keymap.
165  * Typically a look-and-feel implementation will install a different keymap
166  * that resolves to the default keymap for those bindings not found in the
167  * different keymap. The minimal bindings include:
168  * <ul>
169  * <li>inserting content into the editor for the
170  * printable keys.
171  * <li>removing content with the backspace and del
172  * keys.
173  * <li>caret movement forward and backward
174  * </ul>
175  *
176  * <p>
177  * <dt><b><font size=+1>Model/View Split</font></b>
178  * <dd>
179  * The text components have a model-view split. A text component pulls
180  * together the objects used to represent the model, view, and controller.
181  * The text document model may be shared by other views which act as observers
182  * of the model (e.g. a document may be shared by multiple components).
183  *
184  * <p align=center><img SRC="doc-files/editor.gif" alt="Diagram showing interaction between Controller, Document, events, and ViewFactory"
185  * HEIGHT=358 WIDTH=587></p>
186  *
187  * <p>
188  * The model is defined by the {@link Document} interface.
189  * This is intended to provide a flexible text storage mechanism
190  * that tracks change during edits and can be extended to more sophisticated
191  * models. The model interfaces are meant to capture the capabilities of
192  * expression given by SGML, a system used to express a wide variety of
193  * content.
194  * Each modification to the document causes notification of the
195  * details of the change to be sent to all observers in the form of a
196  * {@link DocumentEvent} which allows the views to stay up to date with the model.
197  * This event is sent to observers that have implemented the
198  * {@link DocumentListener}
199  * interface and registered interest with the model being observed.
200  *
201  * <p>
202  * <dt><b><font size=+1>Location Information</font></b>
203  * <dd>
204  * The capability of determining the location of text in
205  * the view is provided. There are two methods, {@link #modelToView}
206  * and {@link #viewToModel} for determining this information.
207  *
208  * <p>
209  * <dt><b><font size=+1>Undo/Redo support</font></b>
210  * <dd>
211  * Support for an edit history mechanism is provided to allow
212  * undo/redo operations. The text component does not itself
213  * provide the history buffer by default, but does provide
214  * the <code>UndoableEdit</code> records that can be used in conjunction
215  * with a history buffer to provide the undo/redo support.
216  * The support is provided by the Document model, which allows
217  * one to attach UndoableEditListener implementations.
218  *
219  * <p>
220  * <dt><b><font size=+1>Thread Safety</font></b>
221  * <dd>
222  * The swing text components provide some support of thread
223  * safe operations. Because of the high level of configurability
224  * of the text components, it is possible to circumvent the
225  * protection provided. The protection primarily comes from
226  * the model, so the documentation of <code>AbstractDocument</code>
227  * describes the assumptions of the protection provided.
228  * The methods that are safe to call asynchronously are marked
229  * with comments.
230  *
231  * <p>
232  * <dt><b><font size=+1>Newlines</font></b>
233  * <dd>
234  * For a discussion on how newlines are handled, see
235  * <a HREF="DefaultEditorKit.html">DefaultEditorKit</a>.
236  * </dl>
237  *
238  * <p>
239  * <strong>Warning:</strong>
240  * Serialized objects of this class will not be compatible with
241  * future Swing releases. The current serialization support is
242  * appropriate for short term storage or RMI between applications running
243  * the same version of Swing. As of 1.4, support for long term storage
244  * of all JavaBeans<sup><font size="-2">TM</font></sup>
245  * has been added to the <code>java.beans</code> package.
246  * Please see {@link java.beans.XMLEncoder}.
247  *
248  * @beaninfo
249  * attribute: isContainer false
250  *
251  * @author Timothy Prinzing
252  * @version 1.213 04/10/06
253  * @see Document
254  * @see DocumentEvent
255  * @see DocumentListener
256  * @see Caret
257  * @see CaretEvent
258  * @see CaretListener
259  * @see TextUI
260  * @see View
261  * @see ViewFactory
262  */

263 public abstract class JTextComponent extends JComponent implements Scrollable, Accessible
264 {
265     /**
266      * Creates a new <code>JTextComponent</code>.
267      * Listeners for caret events are established, and the pluggable
268      * UI installed. The component is marked as editable. No layout manager
269      * is used, because layout is managed by the view subsystem of text.
270      * The document model is set to <code>null</code>.
271      */

272     public JTextComponent() {
273         super();
274     // enable InputMethodEvent for on-the-spot pre-editing
275
enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.INPUT_METHOD_EVENT_MASK);
276         caretEvent = new MutableCaretEvent(this);
277         addMouseListener(caretEvent);
278         addFocusListener(caretEvent);
279         setEditable(true);
280     setDragEnabled(false);
281         setLayout(null); // layout is managed by View hierarchy
282
updateUI();
283     }
284
285     /**
286      * Fetches the user-interface factory for this text-oriented editor.
287      *
288      * @return the factory
289      */

290     public TextUI getUI() { return (TextUI)ui; }
291
292     /**
293      * Sets the user-interface factory for this text-oriented editor.
294      *
295      * @param ui the factory
296      */

297     public void setUI(TextUI ui) {
298         super.setUI(ui);
299     }
300
301     /**
302      * Reloads the pluggable UI. The key used to fetch the
303      * new interface is <code>getUIClassID()</code>. The type of
304      * the UI is <code>TextUI</code>. <code>invalidate</code>
305      * is called after setting the UI.
306      */

307     public void updateUI() {
308         setUI((TextUI)UIManager.getUI(this));
309         invalidate();
310     }
311
312     /**
313      * Adds a caret listener for notification of any changes
314      * to the caret.
315      *
316      * @param listener the listener to be added
317      * @see javax.swing.event.CaretEvent
318      */

319     public void addCaretListener(CaretListener listener) {
320         listenerList.add(CaretListener.class, listener);
321     }
322
323     /**
324      * Removes a caret listener.
325      *
326      * @param listener the listener to be removed
327      * @see javax.swing.event.CaretEvent
328      */

329     public void removeCaretListener(CaretListener listener) {
330         listenerList.remove(CaretListener.class, listener);
331     }
332
333     /**
334      * Returns an array of all the caret listeners
335      * registered on this text component.
336      *
337      * @return all of this component's <code>CaretListener</code>s
338      * or an empty
339      * array if no caret listeners are currently registered
340      *
341      * @see #addCaretListener
342      * @see #removeCaretListener
343      *
344      * @since 1.4
345      */

346     public CaretListener[] getCaretListeners() {
347         return (CaretListener[])listenerList.getListeners(CaretListener.class);
348     }
349
350     /**
351      * Notifies all listeners that have registered interest for
352      * notification on this event type. The event instance
353      * is lazily created using the parameters passed into
354      * the fire method. The listener list is processed in a
355      * last-to-first manner.
356      *
357      * @param e the event
358      * @see EventListenerList
359      */

360     protected void fireCaretUpdate(CaretEvent e) {
361         // Guaranteed to return a non-null array
362
Object JavaDoc[] listeners = listenerList.getListenerList();
363         // Process the listeners last to first, notifying
364
// those that are interested in this event
365
for (int i = listeners.length-2; i>=0; i-=2) {
366             if (listeners[i]==CaretListener.class) {
367                 ((CaretListener)listeners[i+1]).caretUpdate(e);
368             }
369         }
370     }
371
372     /**
373      * Associates the editor with a text document.
374      * The currently registered factory is used to build a view for
375      * the document, which gets displayed by the editor after revalidation.
376      * A PropertyChange event ("document") is propagated to each listener.
377      *
378      * @param doc the document to display/edit
379      * @see #getDocument
380      * @beaninfo
381      * description: the text document model
382      * bound: true
383      * expert: true
384      */

385     public void setDocument(Document JavaDoc doc) {
386         Document JavaDoc old = model;
387
388     /*
389      * aquire a read lock on the old model to prevent notification of
390      * mutations while we disconnecting the old model.
391      */

392     try {
393         if (old instanceof AbstractDocument JavaDoc) {
394         ((AbstractDocument JavaDoc)old).readLock();
395         }
396         if (accessibleContext != null) {
397         model.removeDocumentListener(
398             ((AccessibleJTextComponent)accessibleContext));
399         }
400         if (inputMethodRequestsHandler != null) {
401         model.removeDocumentListener((DocumentListener)inputMethodRequestsHandler);
402         }
403         model = doc;
404
405             // Set the document's run direction property to match the
406
// component's ComponentOrientation property.
407
Boolean JavaDoc runDir = getComponentOrientation().isLeftToRight()
408                              ? TextAttribute.RUN_DIRECTION_LTR
409                              : TextAttribute.RUN_DIRECTION_RTL;
410             doc.putProperty( TextAttribute.RUN_DIRECTION, runDir );
411
412         firePropertyChange("document", old, doc);
413     } finally {
414         if (old instanceof AbstractDocument JavaDoc) {
415         ((AbstractDocument JavaDoc)old).readUnlock();
416         }
417     }
418
419         revalidate();
420         repaint();
421         if (accessibleContext != null) {
422             model.addDocumentListener(
423                 ((AccessibleJTextComponent)accessibleContext));
424         }
425     if (inputMethodRequestsHandler != null) {
426             model.addDocumentListener((DocumentListener)inputMethodRequestsHandler);
427         }
428     }
429
430     /**
431      * Fetches the model associated with the editor. This is
432      * primarily for the UI to get at the minimal amount of
433      * state required to be a text editor. Subclasses will
434      * return the actual type of the model which will typically
435      * be something that extends Document.
436      *
437      * @return the model
438      */

439     public Document JavaDoc getDocument() {
440         return model;
441     }
442
443     // Override of Component.setComponentOrientation
444
public void setComponentOrientation( ComponentOrientation o ) {
445         // Set the document's run direction property to match the
446
// ComponentOrientation property.
447
Document JavaDoc doc = getDocument();
448         if( doc != null ) {
449             Boolean JavaDoc runDir = o.isLeftToRight()
450                              ? TextAttribute.RUN_DIRECTION_LTR
451                              : TextAttribute.RUN_DIRECTION_RTL;
452             doc.putProperty( TextAttribute.RUN_DIRECTION, runDir );
453         }
454         super.setComponentOrientation( o );
455     }
456
457     /**
458      * Fetches the command list for the editor. This is
459      * the list of commands supported by the plugged-in UI
460      * augmented by the collection of commands that the
461      * editor itself supports. These are useful for binding
462      * to events, such as in a keymap.
463      *
464      * @return the command list
465      */

466     public Action JavaDoc[] getActions() {
467         return getUI().getEditorKit(this).getActions();
468     }
469
470     /**
471      * Sets margin space between the text component's border
472      * and its text. The text component's default <code>Border</code>
473      * object will use this value to create the proper margin.
474      * However, if a non-default border is set on the text component,
475      * it is that <code>Border</code> object's responsibility to create the
476      * appropriate margin space (else this property will effectively
477      * be ignored). This causes a redraw of the component.
478      * A PropertyChange event ("margin") is sent to all listeners.
479      *
480      * @param m the space between the border and the text
481      * @beaninfo
482      * description: desired space between the border and text area
483      * bound: true
484      */

485     public void setMargin(Insets m) {
486         Insets old = margin;
487         margin = m;
488         firePropertyChange("margin", old, m);
489         invalidate();
490     }
491
492     /**
493      * Returns the margin between the text component's border and
494      * its text.
495      *
496      * @return the margin
497      */

498     public Insets getMargin() {
499         return margin;
500     }
501
502     /**
503      * Sets the <code>NavigationFilter</code>. <code>NavigationFilter</code>
504      * is used by <code>DefaultCaret</code> and the default cursor movement
505      * actions as a way to restrict the cursor movement.
506      *
507      * @since 1.4
508      */

509     public void setNavigationFilter(NavigationFilter JavaDoc filter) {
510         navigationFilter = filter;
511     }
512
513     /**
514      * Returns the <code>NavigationFilter</code>. <code>NavigationFilter</code>
515      * is used by <code>DefaultCaret</code> and the default cursor movement
516      * actions as a way to restrict the cursor movement. A null return value
517      * implies the cursor movement and selection should not be restricted.
518      *
519      * @since 1.4
520      * @return the NavigationFilter
521      */

522     public NavigationFilter JavaDoc getNavigationFilter() {
523         return navigationFilter;
524     }
525
526     /**
527      * Fetches the caret that allows text-oriented navigation over
528      * the view.
529      *
530      * @return the caret
531      */

532     public Caret JavaDoc getCaret() {
533         return caret;
534     }
535
536     /**
537      * Sets the caret to be used. By default this will be set
538      * by the UI that gets installed. This can be changed to
539      * a custom caret if desired. Setting the caret results in a
540      * PropertyChange event ("caret") being fired.
541      *
542      * @param c the caret
543      * @see #getCaret
544      * @beaninfo
545      * description: the caret used to select/navigate
546      * bound: true
547      * expert: true
548      */

549     public void setCaret(Caret JavaDoc c) {
550         if (caret != null) {
551             caret.removeChangeListener(caretEvent);
552             caret.deinstall(this);
553         }
554         Caret JavaDoc old = caret;
555         caret = c;
556         if (caret != null) {
557             caret.install(this);
558             caret.addChangeListener(caretEvent);
559         }
560         firePropertyChange("caret", old, caret);
561     }
562
563     /**
564      * Fetches the object responsible for making highlights.
565      *
566      * @return the highlighter
567      */

568     public Highlighter JavaDoc getHighlighter() {
569         return highlighter;
570     }
571
572     /**
573      * Sets the highlighter to be used. By default this will be set
574      * by the UI that gets installed. This can be changed to
575      * a custom highlighter if desired. The highlighter can be set to
576      * <code>null</code> to disable it.
577      * A PropertyChange event ("highlighter") is fired
578      * when a new highlighter is installed.
579      *
580      * @param h the highlighter
581      * @see #getHighlighter
582      * @beaninfo
583      * description: object responsible for background highlights
584      * bound: true
585      * expert: true
586      */

587     public void setHighlighter(Highlighter JavaDoc h) {
588         if (highlighter != null) {
589             highlighter.deinstall(this);
590         }
591         Highlighter JavaDoc old = highlighter;
592         highlighter = h;
593         if (highlighter != null) {
594             highlighter.install(this);
595         }
596         firePropertyChange("highlighter", old, h);
597     }
598
599     /**
600      * Sets the keymap to use for binding events to
601      * actions. Setting to <code>null</code> effectively disables
602      * keyboard input.
603      * A PropertyChange event ("keymap") is fired when a new keymap
604      * is installed.
605      *
606      * @param map the keymap
607      * @see #getKeymap
608      * @beaninfo
609      * description: set of key event to action bindings to use
610      * bound: true
611      */

612     public void setKeymap(Keymap JavaDoc map) {
613         Keymap JavaDoc old = keymap;
614         keymap = map;
615         firePropertyChange("keymap", old, keymap);
616     updateInputMap(old, map);
617     }
618
619     /**
620      * Sets the <code>dragEnabled</code> property,
621      * which must be <code>true</code> to enable
622      * automatic drag handling (the first part of drag and drop)
623      * on this component.
624      * The <code>transferHandler</code> property needs to be set
625      * to a non-<code>null</code> value for the drag to do
626      * anything. The default value of the <code>dragEnabled</code>
627      * property
628      * is <code>false</code>.
629      * <p>
630      * When automatic drag handling is enabled,
631      * most look and feels begin a drag-and-drop operation
632      * whenever the user presses the mouse button over a selection
633      * and then moves the mouse a few pixels.
634      * Setting this property to <code>true</code>
635      * can therefore have a subtle effect on
636      * how selections behave.
637      * <p>
638      * Some look and feels might not support automatic drag and drop;
639      * they will ignore this property. You can work around such
640      * look and feels by modifying the component
641      * to directly call the <code>exportAsDrag</code> method of a
642      * <code>TransferHandler</code>.
643      *
644      * @param b the value to set the <code>dragEnabled</code> property to
645      * @exception HeadlessException if
646      * <code>b</code> is <code>true</code> and
647      * <code>GraphicsEnvironment.isHeadless()</code>
648      * returns <code>true</code>
649      * @see java.awt.GraphicsEnvironment#isHeadless
650      * @see #getDragEnabled
651      * @see #setTransferHandler
652      * @see TransferHandler
653      * @since 1.4
654      *
655      * @beaninfo
656      * description: determines whether automatic drag handling is enabled
657      * bound: false
658      */

659     public void setDragEnabled(boolean b) {
660         if (b && GraphicsEnvironment.isHeadless()) {
661             throw new HeadlessException();
662         }
663     dragEnabled = b;
664     }
665
666     /**
667      * Gets the <code>dragEnabled</code> property.
668      *
669      * @return the value of the <code>dragEnabled</code> property
670      * @see #setDragEnabled
671      * @since 1.4
672      */

673     public boolean getDragEnabled() {
674     return dragEnabled;
675     }
676
677
678     /**
679      * Updates the <code>InputMap</code>s in response to a
680      * <code>Keymap</code> change.
681      * @param oldKm the old <code>Keymap</code>
682      * @param newKm the new <code>Keymap</code>
683      */

684     void updateInputMap(Keymap JavaDoc oldKm, Keymap JavaDoc newKm) {
685     // Locate the current KeymapWrapper.
686
InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
687     InputMap last = km;
688     while (km != null && !(km instanceof KeymapWrapper)) {
689         last = km;
690         km = km.getParent();
691     }
692     if (km != null) {
693         // Found it, tweak the InputMap that points to it, as well
694
// as anything it points to.
695
if (newKm == null) {
696         if (last != km) {
697             last.setParent(km.getParent());
698         }
699         else {
700             last.setParent(null);
701         }
702         }
703         else {
704         InputMap newKM = new KeymapWrapper(newKm);
705         last.setParent(newKM);
706         if (last != km) {
707             newKM.setParent(km.getParent());
708         }
709         }
710     }
711     else if (newKm != null) {
712         km = getInputMap(JComponent.WHEN_FOCUSED);
713         if (km != null) {
714         // Couldn't find it.
715
// Set the parent of WHEN_FOCUSED InputMap to be the new one.
716
InputMap newKM = new KeymapWrapper(newKm);
717         newKM.setParent(km.getParent());
718         km.setParent(newKM);
719         }
720     }
721
722     // Do the same thing with the ActionMap
723
ActionMap am = getActionMap();
724     ActionMap lastAM = am;
725     while (am != null && !(am instanceof KeymapActionMap)) {
726         lastAM = am;
727         am = am.getParent();
728     }
729     if (am != null) {
730         // Found it, tweak the Actionap that points to it, as well
731
// as anything it points to.
732
if (newKm == null) {
733         if (lastAM != am) {
734             lastAM.setParent(am.getParent());
735         }
736         else {
737             lastAM.setParent(null);
738         }
739         }
740         else {
741         ActionMap newAM = new KeymapActionMap(newKm);
742         lastAM.setParent(newAM);
743         if (lastAM != am) {
744             newAM.setParent(am.getParent());
745         }
746         }
747     }
748     else if (newKm != null) {
749         am = getActionMap();
750         if (am != null) {
751         // Couldn't find it.
752
// Set the parent of ActionMap to be the new one.
753
ActionMap newAM = new KeymapActionMap(newKm);
754         newAM.setParent(am.getParent());
755         am.setParent(newAM);
756         }
757     }
758     }
759
760     /**
761      * Fetches the keymap currently active in this text
762      * component.
763      *
764      * @return the keymap
765      */

766     public Keymap JavaDoc getKeymap() {
767         return keymap;
768     }
769
770     /**
771      * Adds a new keymap into the keymap hierarchy. Keymap bindings
772      * resolve from bottom up so an attribute specified in a child
773      * will override an attribute specified in the parent.
774      *
775      * @param nm the name of the keymap (must be unique within the
776      * collection of named keymaps in the document); the name may
777      * be <code>null</code> if the keymap is unnamed,
778      * but the caller is responsible for managing the reference
779      * returned as an unnamed keymap can't
780      * be fetched by name
781      * @param parent the parent keymap; this may be <code>null</code> if
782      * unspecified bindings need not be resolved in some other keymap
783      * @return the keymap
784      */

785     public static Keymap JavaDoc addKeymap(String JavaDoc nm, Keymap JavaDoc parent) {
786         Keymap JavaDoc map = new DefaultKeymap(nm, parent);
787         if (nm != null) {
788             // add a named keymap, a class of bindings
789
getKeymapTable().put(nm, map);
790         }
791         return map;
792     }
793
794     /**
795      * Removes a named keymap previously added to the document. Keymaps
796      * with <code>null</code> names may not be removed in this way.
797      *
798      * @param nm the name of the keymap to remove
799      * @return the keymap that was removed
800      */

801     public static Keymap JavaDoc removeKeymap(String JavaDoc nm) {
802         return getKeymapTable().remove(nm);
803     }
804
805     /**
806      * Fetches a named keymap previously added to the document.
807      * This does not work with <code>null</code>-named keymaps.
808      *
809      * @param nm the name of the keymap
810      * @return the keymap
811      */

812     public static Keymap JavaDoc getKeymap(String JavaDoc nm) {
813         return getKeymapTable().get(nm);
814     }
815
816     private static HashMap JavaDoc<String JavaDoc,Keymap JavaDoc> getKeymapTable() {
817         AppContext appContext = AppContext.getAppContext();
818         HashMap JavaDoc<String JavaDoc,Keymap JavaDoc> keymapTable =
819             (HashMap JavaDoc<String JavaDoc,Keymap JavaDoc>)appContext.get(KEYMAP_TABLE);
820         if (keymapTable == null) {
821             keymapTable = new HashMap JavaDoc<String JavaDoc,Keymap JavaDoc>(17);
822             appContext.put(KEYMAP_TABLE, keymapTable);
823             //initialize default keymap
824
Keymap JavaDoc binding = addKeymap(DEFAULT_KEYMAP, null);
825             binding.setDefaultAction(new
826                                      DefaultEditorKit.DefaultKeyTypedAction JavaDoc());
827         }
828         return keymapTable;
829     }
830
831     /**
832      * Binding record for creating key bindings.
833      * <p>
834      * <strong>Warning:</strong>
835      * Serialized objects of this class will not be compatible with
836      * future Swing releases. The current serialization support is
837      * appropriate for short term storage or RMI between applications running
838      * the same version of Swing. As of 1.4, support for long term storage
839      * of all JavaBeans<sup><font size="-2">TM</font></sup>
840      * has been added to the <code>java.beans</code> package.
841      * Please see {@link java.beans.XMLEncoder}.
842      */

843     public static class KeyBinding {
844         
845         /**
846          * The key.
847          */

848         public KeyStroke key;
849
850         /**
851          * The name of the action for the key.
852          */

853         public String JavaDoc actionName;
854
855         /**
856          * Creates a new key binding.
857          *
858          * @param key the key
859          * @param actionName the name of the action for the key
860          */

861         public KeyBinding(KeyStroke key, String JavaDoc actionName) {
862             this.key = key;
863             this.actionName = actionName;
864         }
865     }
866
867     /**
868      * <p>
869      * Loads a keymap with a bunch of
870      * bindings. This can be used to take a static table of
871      * definitions and load them into some keymap. The following
872      * example illustrates an example of binding some keys to
873      * the cut, copy, and paste actions associated with a
874      * JTextComponent. A code fragment to accomplish
875      * this might look as follows:
876      * <pre><code>
877      *
878      * static final JTextComponent.KeyBinding[] defaultBindings = {
879      * new JTextComponent.KeyBinding(
880      * KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
881      * DefaultEditorKit.copyAction),
882      * new JTextComponent.KeyBinding(
883      * KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
884      * DefaultEditorKit.pasteAction),
885      * new JTextComponent.KeyBinding(
886      * KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
887      * DefaultEditorKit.cutAction),
888      * };
889      *
890      * JTextComponent c = new JTextPane();
891      * Keymap k = c.getKeymap();
892      * JTextComponent.loadKeymap(k, defaultBindings, c.getActions());
893      *
894      * </code></pre>
895      * The sets of bindings and actions may be empty but must be
896      * non-<code>null</code>.
897      *
898      * @param map the keymap
899      * @param bindings the bindings
900      * @param actions the set of actions
901      */

902     public static void loadKeymap(Keymap JavaDoc map, KeyBinding[] bindings, Action JavaDoc[] actions) {
903         Hashtable JavaDoc h = new Hashtable JavaDoc();
904         for (int i = 0; i < actions.length; i++) {
905             Action JavaDoc a = actions[i];
906             String JavaDoc value = (String JavaDoc)a.getValue(Action.NAME);
907             h.put((value!=null ? value:""), a);
908         }
909         for (int i = 0; i < bindings.length; i++) {
910             Action JavaDoc a = (Action JavaDoc) h.get(bindings[i].actionName);
911             if (a != null) {
912                 map.addActionForKeyStroke(bindings[i].key, a);
913             }
914         }
915     }
916
917     /**
918      * Returns true if <code>klass</code> is NOT a JTextComponent and it or
919      * one of its superclasses (stoping at JTextComponent) overrides
920      * <code>processInputMethodEvent</code>. It is assumed this will be
921      * invoked from within a <code>doPrivileged</code>, and it is also
922      * assumed <code>klass</code> extends <code>JTextComponent</code>.
923      */

924     private static Boolean JavaDoc isProcessInputMethodEventOverridden(Class JavaDoc klass) {
925         if (klass == JTextComponent JavaDoc.class) {
926             return Boolean.FALSE;
927         }
928         Boolean JavaDoc retValue = (Boolean JavaDoc)overrideMap.get(klass.getName());
929
930         if (retValue != null) {
931             return retValue;
932         }
933         Boolean JavaDoc sOverriden = isProcessInputMethodEventOverridden(
934                                        klass.getSuperclass());
935
936         if (sOverriden.booleanValue()) {
937             // If our superclass has overriden it, then by definition klass
938
// overrides it.
939
overrideMap.put(klass.getName(), sOverriden);
940             return sOverriden;
941         }
942         // klass's superclass didn't override it, check for an override in
943
// klass.
944
try {
945             Class JavaDoc[] classes = new Class JavaDoc[1];
946             classes[0] = InputMethodEvent.class;
947
948             Method JavaDoc m = klass.getDeclaredMethod("processInputMethodEvent",
949                                                classes);
950             retValue = Boolean.TRUE;
951         } catch (NoSuchMethodException JavaDoc nsme) {
952             retValue = Boolean.FALSE;
953         }
954         overrideMap.put(klass.getName(), retValue);
955         return retValue;
956     }
957     
958     /**
959      * Fetches the current color used to render the
960      * caret.
961      *
962      * @return the color
963      */

964     public Color getCaretColor() {
965         return caretColor;
966     }
967
968     /**
969      * Sets the current color used to render the caret.
970      * Setting to <code>null</code> effectively restores the default color.
971      * Setting the color results in a PropertyChange event ("caretColor")
972      * being fired.
973      *
974      * @param c the color
975      * @see #getCaretColor
976      * @beaninfo
977      * description: the color used to render the caret
978      * bound: true
979      * preferred: true
980      */

981     public void setCaretColor(Color c) {
982         Color old = caretColor;
983         caretColor = c;
984         firePropertyChange("caretColor", old, caretColor);
985     }
986
987     /**
988      * Fetches the current color used to render the
989      * selection.
990      *
991      * @return the color
992      */

993     public Color getSelectionColor() {
994         return selectionColor;
995     }
996
997     /**
998      * Sets the current color used to render the selection.
999      * Setting the color to <code>null</code> is the same as setting
1000     * <code>Color.white</code>. Setting the color results in a
1001     * PropertyChange event ("selectionColor").
1002     *
1003     * @param c the color
1004     * @see #getSelectionColor
1005     * @beaninfo
1006     * description: color used to render selection background
1007     * bound: true
1008     * preferred: true
1009     */

1010    public void setSelectionColor(Color c) {
1011        Color old = selectionColor;
1012        selectionColor = c;
1013        firePropertyChange("selectionColor", old, selectionColor);
1014    }
1015
1016    /**
1017     * Fetches the current color used to render the
1018     * selected text.
1019     *
1020     * @return the color
1021     */

1022    public Color getSelectedTextColor() {
1023        return selectedTextColor;
1024    }
1025
1026    /**
1027     * Sets the current color used to render the selected text.
1028     * Setting the color to <code>null</code> is the same as
1029     * <code>Color.black</code>. Setting the color results in a
1030     * PropertyChange event ("selectedTextColor") being fired.
1031     *
1032     * @param c the color
1033     * @see #getSelectedTextColor
1034     * @beaninfo
1035     * description: color used to render selected text
1036     * bound: true
1037     * preferred: true
1038     */

1039    public void setSelectedTextColor(Color c) {
1040        Color old = selectedTextColor;
1041        selectedTextColor = c;
1042        firePropertyChange("selectedTextColor", old, selectedTextColor);
1043    }
1044
1045    /**
1046     * Fetches the current color used to render the
1047     * selected text.
1048     *
1049     * @return the color
1050     */

1051    public Color getDisabledTextColor() {
1052        return disabledTextColor;
1053    }
1054
1055    /**
1056     * Sets the current color used to render the
1057     * disabled text. Setting the color fires off a
1058     * PropertyChange event ("disabledTextColor").
1059     *
1060     * @param c the color
1061     * @see #getDisabledTextColor
1062     * @beaninfo
1063     * description: color used to render disabled text
1064     * bound: true
1065     * preferred: true
1066     */

1067    public void setDisabledTextColor(Color c) {
1068        Color old = disabledTextColor;
1069        disabledTextColor = c;
1070        firePropertyChange("disabledTextColor", old, disabledTextColor);
1071    }
1072
1073    /**
1074     * Replaces the currently selected content with new content
1075     * represented by the given string. If there is no selection
1076     * this amounts to an insert of the given text. If there
1077     * is no replacement text this amounts to a removal of the
1078     * current selection.
1079     * <p>
1080     * This is the method that is used by the default implementation
1081     * of the action for inserting content that gets bound to the
1082     * keymap actions.
1083     * <p>
1084     * This method is thread safe, although most Swing methods
1085     * are not. Please see
1086     * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
1087     * and Swing</A> for more information.
1088     *
1089     * @param content the content to replace the selection with
1090     */

1091    public void replaceSelection(String JavaDoc content) {
1092        Document JavaDoc doc = getDocument();
1093        if (doc != null) {
1094            try {
1095        boolean composedTextSaved = saveComposedText(caret.getDot());
1096                int p0 = Math.min(caret.getDot(), caret.getMark());
1097                int p1 = Math.max(caret.getDot(), caret.getMark());
1098                if (doc instanceof AbstractDocument JavaDoc) {
1099                    ((AbstractDocument JavaDoc)doc).replace(p0, p1 - p0, content,null);
1100                }
1101                else {
1102                    if (p0 != p1) {
1103                        doc.remove(p0, p1 - p0);
1104                    }
1105                    if (content != null && content.length() > 0) {
1106                        doc.insertString(p0, content, null);
1107                    }
1108                }
1109        if (composedTextSaved) {
1110            restoreComposedText();
1111        }
1112            } catch (BadLocationException JavaDoc e) {
1113        UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
1114            }
1115        }
1116    }
1117
1118    /**
1119     * Fetches a portion of the text represented by the
1120     * component. Returns an empty string if length is 0.
1121     *
1122     * @param offs the offset >= 0
1123     * @param len the length >= 0
1124     * @return the text
1125     * @exception BadLocationException if the offset or length are invalid
1126     */

1127    public String JavaDoc getText(int offs, int len) throws BadLocationException JavaDoc {
1128        return getDocument().getText(offs, len);
1129    }
1130
1131    /**
1132     * Converts the given location in the model to a place in
1133     * the view coordinate system.
1134     * The component must have a positive size for
1135     * this translation to be computed (i.e. layout cannot
1136     * be computed until the component has been sized). The
1137     * component does not have to be visible or painted.
1138     *
1139     * @param pos the position >= 0
1140     * @return the coordinates as a rectangle, with (r.x, r.y) as the location
1141     * in the coordinate system, or null if the component does
1142     * not yet have a positive size.
1143     * @exception BadLocationException if the given position does not
1144     * represent a valid location in the associated document
1145     * @see TextUI#modelToView
1146     */

1147    public Rectangle modelToView(int pos) throws BadLocationException JavaDoc {
1148        return getUI().modelToView(this, pos);
1149    }
1150
1151    /**
1152     * Converts the given place in the view coordinate system
1153     * to the nearest representative location in the model.
1154     * The component must have a positive size for
1155     * this translation to be computed (i.e. layout cannot
1156     * be computed until the component has been sized). The
1157     * component does not have to be visible or painted.
1158     *
1159     * @param pt the location in the view to translate
1160     * @return the offset >= 0 from the start of the document,
1161     * or -1 if the component does not yet have a positive
1162     * size.
1163     * @see TextUI#viewToModel
1164     */

1165    public int viewToModel(Point pt) {
1166        return getUI().viewToModel(this, pt);
1167    }
1168
1169    /**
1170     * Transfers the currently selected range in the associated
1171     * text model to the system clipboard, removing the contents
1172     * from the model. The current selection is reset. Does nothing
1173     * for <code>null</code> selections.
1174     *
1175     * @see java.awt.Toolkit#getSystemClipboard
1176     * @see java.awt.datatransfer.Clipboard
1177     */

1178    public void cut() {
1179    if (isEditable() && isEnabled()) {
1180            invokeAction("cut", TransferHandler.getCutAction());
1181    }
1182    }
1183
1184    /**
1185     * Transfers the currently selected range in the associated
1186     * text model to the system clipboard, leaving the contents
1187     * in the text model. The current selection remains intact.
1188     * Does nothing for <code>null</code> selections.
1189     *
1190     * @see java.awt.Toolkit#getSystemClipboard
1191     * @see java.awt.datatransfer.Clipboard
1192     */

1193    public void copy() {
1194        invokeAction("copy", TransferHandler.getCopyAction());
1195    }
1196    
1197    /**
1198     * Transfers the contents of the system clipboard into the
1199     * associated text model. If there is a selection in the
1200     * associated view, it is replaced with the contents of the
1201     * clipboard. If there is no selection, the clipboard contents
1202     * are inserted in front of the current insert position in
1203     * the associated view. If the clipboard is empty, does nothing.
1204     *
1205     * @see #replaceSelection
1206     * @see java.awt.Toolkit#getSystemClipboard
1207     * @see java.awt.datatransfer.Clipboard
1208     */

1209    public void paste() {
1210    if (isEditable() && isEnabled()) {
1211            invokeAction("paste", TransferHandler.getPasteAction());
1212    }
1213    }
1214
1215    /**
1216     * This is a conveniance method that is only useful for
1217     * <code>cut</code>, <code>copy</code> and <code>paste</code>. If
1218     * an <code>Action</code> with the name <code>name</code> does not
1219     * exist in the <code>ActionMap</code>, this will attemp to install a
1220     * <code>TransferHandler</code> and then use <code>altAction</code>.
1221     */

1222    private void invokeAction(String JavaDoc name, Action JavaDoc altAction) {
1223        ActionMap map = getActionMap();
1224        Action JavaDoc action = null;
1225
1226        if (map != null) {
1227            action = map.get(name);
1228        }
1229        if (action == null) {
1230            installDefaultTransferHandlerIfNecessary();
1231            action = altAction;
1232        }
1233        action.actionPerformed(new ActionEvent(this,
1234                               ActionEvent.ACTION_PERFORMED, (String JavaDoc)action.
1235                               getValue(Action.NAME),
1236                               EventQueue.getMostRecentEventTime(),
1237                               getCurrentEventModifiers()));
1238    }
1239
1240    /**
1241     * If the current <code>TransferHandler</code> is null, this will
1242     * install a new one.
1243     */

1244    private void installDefaultTransferHandlerIfNecessary() {
1245        if (getTransferHandler() == null) {
1246            if (defaultTransferHandler == null) {
1247                defaultTransferHandler = new DefaultTransferHandler();
1248            }
1249            setTransferHandler(defaultTransferHandler);
1250        }
1251    }
1252
1253    /**
1254     * Moves the caret to a new position, leaving behind a mark
1255     * defined by the last time <code>setCaretPosition</code> was
1256     * called. This forms a selection.
1257     * If the document is <code>null</code>, does nothing. The position
1258     * must be between 0 and the length of the component's text or else
1259     * an exception is thrown.
1260     *
1261     * @param pos the position
1262     * @exception IllegalArgumentException if the value supplied
1263     * for <code>position</code> is less than zero or greater
1264     * than the component's text length
1265     * @see #setCaretPosition
1266     */

1267    public void moveCaretPosition(int pos) {
1268        Document JavaDoc doc = getDocument();
1269        if (doc != null) {
1270            if (pos > doc.getLength() || pos < 0) {
1271                throw new IllegalArgumentException JavaDoc("bad position: " + pos);
1272            }
1273            caret.moveDot(pos);
1274        }
1275    }
1276
1277    /**
1278     * The bound property name for the focus accelerator.
1279     */

1280    public static final String JavaDoc FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
1281
1282    /**
1283     * Sets the key accelerator that will cause the receiving text
1284     * component to get the focus. The accelerator will be the
1285     * key combination of the <em>alt</em> key and the character
1286     * given (converted to upper case). By default, there is no focus
1287     * accelerator key. Any previous key accelerator setting will be
1288     * superseded. A '\0' key setting will be registered, and has the
1289     * effect of turning off the focus accelerator. When the new key
1290     * is set, a PropertyChange event (FOCUS_ACCELERATOR_KEY) will be fired.
1291     *
1292     * @param aKey the key
1293     * @see #getFocusAccelerator
1294     * @beaninfo
1295     * description: accelerator character used to grab focus
1296     * bound: true
1297     */

1298    public void setFocusAccelerator(char aKey) {
1299        aKey = Character.toUpperCase(aKey);
1300        char old = focusAccelerator;
1301        focusAccelerator = aKey;
1302        // Fix for 4341002: value of FOCUS_ACCELERATOR_KEY is wrong.
1303
// So we fire both FOCUS_ACCELERATOR_KEY, for compatibility,
1304
// and the correct event here.
1305
firePropertyChange(FOCUS_ACCELERATOR_KEY, old, focusAccelerator);
1306        firePropertyChange("focusAccelerator", old, focusAccelerator);
1307    }
1308
1309    /**
1310     * Returns the key accelerator that will cause the receiving
1311     * text component to get the focus. Return '\0' if no focus
1312     * accelerator has been set.
1313     *
1314     * @return the key
1315     */

1316    public char getFocusAccelerator() {
1317        return focusAccelerator;
1318    }
1319
1320    /**
1321     * Initializes from a stream. This creates a
1322     * model of the type appropriate for the component
1323     * and initializes the model from the stream.
1324     * By default this will load the model as plain
1325     * text. Previous contents of the model are discarded.
1326     *
1327     * @param in the stream to read from
1328     * @param desc an object describing the stream; this
1329     * might be a string, a File, a URL, etc. Some kinds
1330     * of documents (such as html for example) might be
1331     * able to make use of this information; if non-<code>null</code>,
1332     * it is added as a property of the document
1333     * @exception IOException as thrown by the stream being
1334     * used to initialize
1335     * @see EditorKit#createDefaultDocument
1336     * @see #setDocument
1337     * @see PlainDocument
1338     */

1339    public void read(Reader in, Object JavaDoc desc) throws IOException {
1340        EditorKit JavaDoc kit = getUI().getEditorKit(this);
1341        Document JavaDoc doc = kit.createDefaultDocument();
1342        if (desc != null) {
1343            doc.putProperty(Document.StreamDescriptionProperty, desc);
1344        }
1345        try {
1346            kit.read(in, doc, 0);
1347            setDocument(doc);
1348        } catch (BadLocationException JavaDoc e) {
1349            throw new IOException(e.getMessage());
1350        }
1351    }
1352
1353    /**
1354     * Stores the contents of the model into the given
1355     * stream. By default this will store the model as plain
1356     * text.
1357     *
1358     * @param out the output stream
1359     * @exception IOException on any I/O error
1360     */

1361    public void write(Writer out) throws IOException {
1362        Document JavaDoc doc = getDocument();
1363        try {
1364            getUI().getEditorKit(this).write(out, doc, 0, doc.getLength());
1365        } catch (BadLocationException JavaDoc e) {
1366            throw new IOException(e.getMessage());
1367        }
1368    }
1369
1370    public void removeNotify() {
1371        super.removeNotify();
1372        if (getFocusedComponent() == this) {
1373            AppContext.getAppContext().remove(FOCUSED_COMPONENT);
1374        }
1375    }
1376
1377    // --- java.awt.TextComponent methods ------------------------
1378

1379    /**
1380     * Sets the position of the text insertion caret for the
1381     * <code>TextComponent</code>. Note that the caret tracks change,
1382     * so this may move if the underlying text of the component is changed.
1383     * If the document is <code>null</code>, does nothing. The position
1384     * must be between 0 and the length of the component's text or else
1385     * an exception is thrown.
1386     *
1387     * @param position the position
1388     * @exception IllegalArgumentException if the value supplied
1389     * for <code>position</code> is less than zero or greater
1390     * than the component's text length
1391     * @beaninfo
1392     * description: the caret position
1393     */

1394    public void setCaretPosition(int position) {
1395        Document JavaDoc doc = getDocument();
1396        if (doc != null) {
1397        if (position > doc.getLength() || position < 0) {
1398        throw new IllegalArgumentException JavaDoc("bad position: " + position);
1399        }
1400            caret.setDot(position);
1401        }
1402    }
1403
1404    /**
1405     * Returns the position of the text insertion caret for the
1406     * text component.
1407     *
1408     * @return the position of the text insertion caret for the
1409     * text component >= 0
1410     */

1411    public int getCaretPosition() {
1412        return caret.getDot();
1413    }
1414
1415    /**
1416     * Sets the text of this <code>TextComponent</code>
1417     * to the specified text. If the text is <code>null</code>
1418     * or empty, has the effect of simply deleting the old text.
1419     * When text has been inserted, the resulting caret location
1420     * is determined by the implementation of the caret class.
1421     * <p>
1422     * This method is thread safe, although most Swing methods
1423     * are not. Please see
1424     * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
1425     * and Swing</A> for more information.
1426     *
1427     * Note that text is not a bound property, so no <code>PropertyChangeEvent
1428     * </code> is fired when it changes. To listen for changes to the text,
1429     * use <code>DocumentListener</code>.
1430     *
1431     * @param t the new text to be set
1432     * @see #getText
1433     * @see DefaultCaret
1434     * @beaninfo
1435     * description: the text of this component
1436     */

1437    public void setText(String JavaDoc t) {
1438        try {
1439            Document JavaDoc doc = getDocument();
1440            if (doc instanceof AbstractDocument JavaDoc) {
1441                ((AbstractDocument JavaDoc)doc).replace(0, doc.getLength(), t,null);
1442            }
1443            else {
1444                doc.remove(0, doc.getLength());
1445                doc.insertString(0, t, null);
1446            }
1447        } catch (BadLocationException JavaDoc e) {
1448        UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
1449        }
1450    }
1451
1452    /**
1453     * Returns the text contained in this <code>TextComponent</code>.
1454     * If the underlying document is <code>null</code>,
1455     * will give a <code>NullPointerException</code>.
1456     *
1457     * Note that text is not a bound property, so no <code>PropertyChangeEvent
1458     * </code> is fired when it changes. To listen for changes to the text,
1459     * use <code>DocumentListener</code>.
1460     *
1461     * @return the text
1462     * @exception NullPointerException if the document is <code>null</code>
1463     * @see #setText
1464     */

1465    public String JavaDoc getText() {
1466        Document JavaDoc doc = getDocument();
1467        String JavaDoc txt;
1468        try {
1469            txt = doc.getText(0, doc.getLength());
1470        } catch (BadLocationException JavaDoc e) {
1471            txt = null;
1472        }
1473        return txt;
1474    }
1475
1476    /**
1477     * Returns the selected text contained in this
1478     * <code>TextComponent</code>. If the selection is
1479     * <code>null</code> or the document empty, returns <code>null</code>.
1480     *
1481     * @return the text
1482     * @exception IllegalArgumentException if the selection doesn't
1483     * have a valid mapping into the document for some reason
1484     * @see #setText
1485     */

1486    public String JavaDoc getSelectedText() {
1487        String JavaDoc txt = null;
1488        int p0 = Math.min(caret.getDot(), caret.getMark());
1489        int p1 = Math.max(caret.getDot(), caret.getMark());
1490        if (p0 != p1) {
1491            try {
1492                Document JavaDoc doc = getDocument();
1493                txt = doc.getText(p0, p1 - p0);
1494            } catch (BadLocationException JavaDoc e) {
1495        throw new IllegalArgumentException JavaDoc(e.getMessage());
1496            }
1497        }
1498        return txt;
1499    }
1500
1501    /**
1502     * Returns the boolean indicating whether this
1503     * <code>TextComponent</code> is editable or not.
1504     *
1505     * @return the boolean value
1506     * @see #setEditable
1507     */

1508    public boolean isEditable() {
1509        return editable;
1510    }
1511
1512    /**
1513     * Sets the specified boolean to indicate whether or not this
1514     * <code>TextComponent</code> should be editable.
1515     * A PropertyChange event ("editable") is fired when the
1516     * state is changed.
1517     *
1518     * @param b the boolean to be set
1519     * @see #isEditable
1520     * @beaninfo
1521     * description: specifies if the text can be edited
1522     * bound: true
1523     */

1524    public void setEditable(boolean b) {
1525    if (b != editable) {
1526        boolean oldVal = editable;
1527        editable = b;
1528        if (editable) {
1529        setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
1530        } else {
1531        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1532        }
1533        enableInputMethods(editable);
1534        firePropertyChange("editable", Boolean.valueOf(oldVal), Boolean.valueOf(editable));
1535        repaint();
1536    }
1537    }
1538
1539    /**
1540     * Returns the selected text's start position. Return 0 for an
1541     * empty document, or the value of dot if no selection.
1542     *
1543     * @return the start position >= 0
1544     */

1545    public int getSelectionStart() {
1546        int start = Math.min(caret.getDot(), caret.getMark());
1547        return start;
1548    }
1549
1550    /**
1551     * Sets the selection start to the specified position. The new
1552     * starting point is constrained to be before or at the current
1553     * selection end.
1554     * <p>
1555     * This is available for backward compatibility to code
1556     * that called this method on <code>java.awt.TextComponent</code>.
1557     * This is implemented to forward to the <code>Caret</code>
1558     * implementation which is where the actual selection is maintained.
1559     *
1560     * @param selectionStart the start position of the text >= 0
1561     * @beaninfo
1562     * description: starting location of the selection.
1563     */

1564    public void setSelectionStart(int selectionStart) {
1565        /* Route through select method to enforce consistent policy
1566         * between selectionStart and selectionEnd.
1567         */

1568        select(selectionStart, getSelectionEnd());
1569    }
1570
1571    /**
1572     * Returns the selected text's end position. Return 0 if the document
1573     * is empty, or the value of dot if there is no selection.
1574     *
1575     * @return the end position >= 0
1576     */

1577    public int getSelectionEnd() {
1578        int end = Math.max(caret.getDot(), caret.getMark());
1579        return end;
1580    }
1581
1582    /**
1583     * Sets the selection end to the specified position. The new
1584     * end point is constrained to be at or after the current
1585     * selection start.
1586     * <p>
1587     * This is available for backward compatibility to code
1588     * that called this method on <code>java.awt.TextComponent</code>.
1589     * This is implemented to forward to the <code>Caret</code>
1590     * implementation which is where the actual selection is maintained.
1591     *
1592     * @param selectionEnd the end position of the text >= 0
1593     * @beaninfo
1594     * description: ending location of the selection.
1595     */

1596    public void setSelectionEnd(int selectionEnd) {
1597        /* Route through select method to enforce consistent policy
1598         * between selectionStart and selectionEnd.
1599         */

1600        select(getSelectionStart(), selectionEnd);
1601    }
1602    
1603    /**
1604     * Selects the text between the specified start and end positions.
1605     * <p>
1606     * This method sets the start and end positions of the
1607     * selected text, enforcing the restriction that the start position
1608     * must be greater than or equal to zero. The end position must be
1609     * greater than or equal to the start position, and less than or
1610     * equal to the length of the text component's text.
1611     * <p>
1612     * If the caller supplies values that are inconsistent or out of
1613     * bounds, the method enforces these constraints silently, and
1614     * without failure. Specifically, if the start position or end
1615     * position is greater than the length of the text, it is reset to
1616     * equal the text length. If the start position is less than zero,
1617     * it is reset to zero, and if the end position is less than the
1618     * start position, it is reset to the start position.
1619     * <p>
1620     * This call is provided for backward compatibility.
1621     * It is routed to a call to <code>setCaretPosition</code>
1622     * followed by a call to <code>moveCaretPosition</code>.
1623     * The preferred way to manage selection is by calling
1624     * those methods directly.
1625     *
1626     * @param selectionStart the start position of the text
1627     * @param selectionEnd the end position of the text
1628     * @see #setCaretPosition
1629     * @see #moveCaretPosition
1630     */

1631    public void select(int selectionStart, int selectionEnd) {
1632    // argument adjustment done by java.awt.TextComponent
1633
int docLength = getDocument().getLength();
1634        
1635    if (selectionStart < 0) {
1636        selectionStart = 0;
1637    }
1638        if (selectionStart > docLength) {
1639            selectionStart = docLength;
1640        }
1641        if (selectionEnd > docLength) {
1642            selectionEnd = docLength;
1643    }
1644    if (selectionEnd < selectionStart) {
1645        selectionEnd = selectionStart;
1646    }
1647
1648        setCaretPosition(selectionStart);
1649        moveCaretPosition(selectionEnd);
1650    }
1651
1652    /**
1653     * Selects all the text in the <code>TextComponent</code>.
1654     * Does nothing on a <code>null</code> or empty document.
1655     */

1656    public void selectAll() {
1657        Document JavaDoc doc = getDocument();
1658        if (doc != null) {
1659            setCaretPosition(0);
1660            moveCaretPosition(doc.getLength());
1661        }
1662    }
1663
1664    // --- Tooltip Methods ---------------------------------------------
1665

1666    /**
1667     * Returns the string to be used as the tooltip for <code>event</code>.
1668     * This will return one of:
1669     * <ol>
1670     * <li>If <code>setToolTipText</code> has been invoked with a
1671     * non-<code>null</code>
1672     * value, it will be returned, otherwise
1673     * <li>The value from invoking <code>getToolTipText</code> on
1674     * the UI will be returned.
1675     * </ol>
1676     * By default <code>JTextComponent</code> does not register
1677     * itself with the <code>ToolTipManager</code>.
1678     * This means that tooltips will NOT be shown from the
1679     * <code>TextUI</code> unless <code>registerComponent</code> has
1680     * been invoked on the <code>ToolTipManager</code>.
1681     *
1682     * @param event the event in question
1683     * @return the string to be used as the tooltip for <code>event</code>
1684     * @see javax.swing.JComponent#setToolTipText
1685     * @see javax.swing.plaf.TextUI#getToolTipText
1686     * @see javax.swing.ToolTipManager#registerComponent
1687     */

1688    public String JavaDoc getToolTipText(MouseEvent event) {
1689        String JavaDoc retValue = super.getToolTipText(event);
1690
1691        if (retValue == null) {
1692            TextUI ui = getUI();
1693            if (ui != null) {
1694                retValue = ui.getToolTipText(this, new Point(event.getX(),
1695                                                             event.getY()));
1696            }
1697        }
1698        return retValue;
1699    }
1700
1701    // --- Scrollable methods ---------------------------------------------
1702

1703    /**
1704     * Returns the preferred size of the viewport for a view component.
1705     * This is implemented to do the default behavior of returning
1706     * the preferred size of the component.
1707     *
1708     * @return the <code>preferredSize</code> of a <code>JViewport</code>
1709     * whose view is this <code>Scrollable</code>
1710     */

1711    public Dimension getPreferredScrollableViewportSize() {
1712        return getPreferredSize();
1713    }
1714
1715
1716    /**
1717     * Components that display logical rows or columns should compute
1718     * the scroll increment that will completely expose one new row
1719     * or column, depending on the value of orientation. Ideally,
1720     * components should handle a partially exposed row or column by
1721     * returning the distance required to completely expose the item.
1722     * <p>
1723     * The default implementation of this is to simply return 10% of
1724     * the visible area. Subclasses are likely to be able to provide
1725     * a much more reasonable value.
1726     *
1727     * @param visibleRect the view area visible within the viewport
1728     * @param orientation either <code>SwingConstants.VERTICAL</code> or
1729     * <code>SwingConstants.HORIZONTAL</code>
1730     * @param direction less than zero to scroll up/left, greater than
1731     * zero for down/right
1732     * @return the "unit" increment for scrolling in the specified direction
1733     * @exception IllegalArgumentException for an invalid orientation
1734     * @see JScrollBar#setUnitIncrement
1735     */

1736    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
1737        switch(orientation) {
1738        case SwingConstants.VERTICAL:
1739            return visibleRect.height / 10;
1740        case SwingConstants.HORIZONTAL:
1741            return visibleRect.width / 10;
1742        default:
1743            throw new IllegalArgumentException JavaDoc("Invalid orientation: " + orientation);
1744        }
1745    }
1746
1747
1748    /**
1749     * Components that display logical rows or columns should compute
1750     * the scroll increment that will completely expose one block
1751     * of rows or columns, depending on the value of orientation.
1752     * <p>
1753     * The default implementation of this is to simply return the visible
1754     * area. Subclasses will likely be able to provide a much more
1755     * reasonable value.
1756     *
1757     * @param visibleRect the view area visible within the viewport
1758     * @param orientation either <code>SwingConstants.VERTICAL</code> or
1759     * <code>SwingConstants.HORIZONTAL</code>
1760     * @param direction less than zero to scroll up/left, greater than zero
1761     * for down/right
1762     * @return the "block" increment for scrolling in the specified direction
1763     * @exception IllegalArgumentException for an invalid orientation
1764     * @see JScrollBar#setBlockIncrement
1765     */

1766    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
1767        switch(orientation) {
1768        case SwingConstants.VERTICAL:
1769            return visibleRect.height;
1770        case SwingConstants.HORIZONTAL:
1771            return visibleRect.width;
1772        default:
1773            throw new IllegalArgumentException JavaDoc("Invalid orientation: " + orientation);
1774        }
1775    }
1776    
1777
1778    /**
1779     * Returns true if a viewport should always force the width of this
1780     * <code>Scrollable</code> to match the width of the viewport.
1781     * For example a normal text view that supported line wrapping
1782     * would return true here, since it would be undesirable for
1783     * wrapped lines to disappear beyond the right
1784     * edge of the viewport. Note that returning true for a
1785     * <code>Scrollable</code> whose ancestor is a <code>JScrollPane</code>
1786     * effectively disables horizontal scrolling.
1787     * <p>
1788     * Scrolling containers, like <code>JViewport</code>,
1789     * will use this method each time they are validated.
1790     *
1791     * @return true if a viewport should force the <code>Scrollable</code>s
1792     * width to match its own
1793     */

1794    public boolean getScrollableTracksViewportWidth() {
1795    if (getParent() instanceof JViewport) {
1796        return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
1797    }
1798    return false;
1799    }
1800
1801    /**
1802     * Returns true if a viewport should always force the height of this
1803     * <code>Scrollable</code> to match the height of the viewport.
1804     * For example a columnar text view that flowed text in left to
1805     * right columns could effectively disable vertical scrolling by
1806     * returning true here.
1807     * <p>
1808     * Scrolling containers, like <code>JViewport</code>,
1809     * will use this method each time they are validated.
1810     *
1811     * @return true if a viewport should force the Scrollables height
1812     * to match its own
1813     */

1814    public boolean getScrollableTracksViewportHeight() {
1815    if (getParent() instanceof JViewport) {
1816        return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
1817    }
1818    return false;
1819    }
1820
1821/////////////////
1822
// Accessibility support
1823
////////////////
1824

1825
1826    /**
1827     * Gets the <code>AccessibleContext</code> associated with this
1828     * <code>JTextComponent</code>. For text components,
1829     * the <code>AccessibleContext</code> takes the form of an
1830     * <code>AccessibleJTextComponent</code>.
1831     * A new <code>AccessibleJTextComponent</code> instance
1832     * is created if necessary.
1833     *
1834     * @return an <code>AccessibleJTextComponent</code> that serves as the
1835     * <code>AccessibleContext</code> of this
1836     * <code>JTextComponent</code>
1837     */

1838    public AccessibleContext getAccessibleContext() {
1839        if (accessibleContext == null) {
1840            accessibleContext = new AccessibleJTextComponent();
1841        }
1842        return accessibleContext;
1843    }
1844
1845    /**
1846     * This class implements accessibility support for the
1847     * <code>JTextComponent</code> class. It provides an implementation of
1848     * the Java Accessibility API appropriate to menu user-interface elements.
1849     * <p>
1850     * <strong>Warning:</strong>
1851     * Serialized objects of this class will not be compatible with
1852     * future Swing releases. The current serialization support is
1853     * appropriate for short term storage or RMI between applications running
1854     * the same version of Swing. As of 1.4, support for long term storage
1855     * of all JavaBeans<sup><font size="-2">TM</font></sup>
1856     * has been added to the <code>java.beans</code> package.
1857     * Please see {@link java.beans.XMLEncoder}.
1858     */

1859    public class AccessibleJTextComponent extends AccessibleJComponent
1860    implements AccessibleText, CaretListener, DocumentListener,
1861               AccessibleAction, AccessibleEditableText {
1862
1863        int caretPos;
1864        Point oldLocationOnScreen;
1865
1866        /**
1867         * Constructs an AccessibleJTextComponent. Adds a listener to track
1868         * caret change.
1869         */

1870        public AccessibleJTextComponent() {
1871            Document JavaDoc doc = JTextComponent.this.getDocument();
1872            if (doc != null) {
1873                doc.addDocumentListener(this);
1874            }
1875            JTextComponent.this.addCaretListener(this);
1876            caretPos = getCaretPosition();
1877
1878            try {
1879            oldLocationOnScreen = getLocationOnScreen();
1880            } catch (IllegalComponentStateException iae) {
1881            }
1882
1883        // Fire a ACCESSIBLE_VISIBLE_DATA_PROPERTY PropertyChangeEvent
1884
// when the text component moves (e.g., when scrolling).
1885
// Using an anonymous class since making AccessibleJTextComponent
1886
// implement ComponentListener would be an API change.
1887
JTextComponent.this.addComponentListener(new ComponentAdapter() {
1888
1889            public void componentMoved(ComponentEvent e) {
1890                    try {
1891                Point newLocationOnScreen = getLocationOnScreen();
1892                firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY,
1893                           oldLocationOnScreen,
1894                           newLocationOnScreen);
1895            
1896                oldLocationOnScreen = newLocationOnScreen;
1897                    } catch (IllegalComponentStateException iae) {
1898                    }
1899        }
1900        });
1901        }
1902
1903        /**
1904         * Handles caret updates (fire appropriate property change event,
1905         * which are AccessibleContext.ACCESSIBLE_CARET_PROPERTY and
1906         * AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY).
1907         * This keeps track of the dot position internally. When the caret
1908         * moves, the internal position is updated after firing the event.
1909         *
1910         * @param e the CaretEvent
1911         */

1912        public void caretUpdate(CaretEvent e) {
1913            int dot = e.getDot();
1914            int mark = e.getMark();
1915            if (caretPos != dot) {
1916                // the caret moved
1917
firePropertyChange(ACCESSIBLE_CARET_PROPERTY,
1918                    new Integer JavaDoc(caretPos), new Integer JavaDoc(dot));
1919                caretPos = dot;
1920
1921                try {
1922                    oldLocationOnScreen = getLocationOnScreen();
1923                } catch (IllegalComponentStateException iae) {
1924                }
1925            }
1926            if (mark != dot) {
1927                // there is a selection
1928
firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
1929                    getSelectedText());
1930            }
1931        }
1932
1933        // DocumentListener methods
1934

1935        /**
1936         * Handles document insert (fire appropriate property change event
1937         * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
1938         * This tracks the changed offset via the event.
1939         *
1940         * @param e the DocumentEvent
1941         */

1942        public void insertUpdate(DocumentEvent e) {
1943            final Integer JavaDoc pos = new Integer JavaDoc (e.getOffset());
1944            if (SwingUtilities.isEventDispatchThread()) {
1945                firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
1946            } else {
1947                Runnable JavaDoc doFire = new Runnable JavaDoc() {
1948                    public void run() {
1949                        firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
1950                                           null, pos);
1951                    }
1952                };
1953                SwingUtilities.invokeLater(doFire);
1954            }
1955        }
1956
1957        /**
1958         * Handles document remove (fire appropriate property change event,
1959         * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
1960         * This tracks the changed offset via the event.
1961         *
1962         * @param e the DocumentEvent
1963         */

1964        public void removeUpdate(DocumentEvent e) {
1965            final Integer JavaDoc pos = new Integer JavaDoc (e.getOffset());
1966            if (SwingUtilities.isEventDispatchThread()) {
1967                firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
1968            } else {
1969                Runnable JavaDoc doFire = new Runnable JavaDoc() {
1970                    public void run() {
1971                        firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
1972                                           null, pos);
1973                    }
1974                };
1975                SwingUtilities.invokeLater(doFire);
1976            }
1977        }
1978
1979        /**
1980         * Handles document remove (fire appropriate property change event,
1981         * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
1982         * This tracks the changed offset via the event.
1983         *
1984         * @param e the DocumentEvent
1985         */

1986        public void changedUpdate(DocumentEvent e) {
1987            final Integer JavaDoc pos = new Integer JavaDoc (e.getOffset());
1988            if (SwingUtilities.isEventDispatchThread()) {
1989                firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
1990            } else {
1991                Runnable JavaDoc doFire = new Runnable JavaDoc() {
1992                    public void run() {
1993                        firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
1994                                           null, pos);
1995                    }
1996                };
1997                SwingUtilities.invokeLater(doFire);
1998            }
1999        }
2000
2001        /**
2002         * Gets the state set of the JTextComponent.
2003         * The AccessibleStateSet of an object is composed of a set of
2004         * unique AccessibleState's. A change in the AccessibleStateSet
2005         * of an object will cause a PropertyChangeEvent to be fired
2006         * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
2007         *
2008         * @return an instance of AccessibleStateSet containing the
2009         * current state set of the object
2010         * @see AccessibleStateSet
2011         * @see AccessibleState
2012         * @see #addPropertyChangeListener
2013         */

2014        public AccessibleStateSet getAccessibleStateSet() {
2015            AccessibleStateSet states = super.getAccessibleStateSet();
2016            if (JTextComponent.this.isEditable()) {
2017                states.add(AccessibleState.EDITABLE);
2018            }
2019            return states;
2020        }
2021
2022
2023        /**
2024         * Gets the role of this object.
2025         *
2026         * @return an instance of AccessibleRole describing the role of the
2027         * object (AccessibleRole.TEXT)
2028         * @see AccessibleRole
2029         */

2030        public AccessibleRole getAccessibleRole() {
2031            return AccessibleRole.TEXT;
2032        }
2033
2034        /**
2035         * Get the AccessibleText associated with this object. In the
2036         * implementation of the Java Accessibility API for this class,
2037         * return this object, which is responsible for implementing the
2038         * AccessibleText interface on behalf of itself.
2039         *
2040         * @return this object
2041         */

2042        public AccessibleText getAccessibleText() {
2043            return this;
2044        }
2045
2046
2047        // --- interface AccessibleText methods ------------------------
2048

2049        /**
2050         * Many of these methods are just convenience methods; they
2051         * just call the equivalent on the parent
2052         */

2053
2054        /**
2055         * Given a point in local coordinates, return the zero-based index
2056         * of the character under that Point. If the point is invalid,
2057         * this method returns -1.
2058         *
2059         * @param p the Point in local coordinates
2060         * @return the zero-based index of the character under Point p.
2061         */

2062        public int getIndexAtPoint(Point p) {
2063            if (p == null) {
2064                return -1;
2065            }
2066            return JTextComponent.this.viewToModel(p);
2067        }
2068
2069        /**
2070         * Gets the editor's drawing rectangle. Stolen
2071         * from the unfortunately named
2072         * BasicTextUI.getVisibleEditorRect()
2073         *
2074         * @return the bounding box for the root view
2075         */

2076        Rectangle getRootEditorRect() {
2077            Rectangle alloc = JTextComponent.this.getBounds();
2078            if ((alloc.width > 0) && (alloc.height > 0)) {
2079                alloc.x = alloc.y = 0;
2080                Insets insets = JTextComponent.this.getInsets();
2081                alloc.x += insets.left;
2082                alloc.y += insets.top;
2083                alloc.width -= insets.left + insets.right;
2084                alloc.height -= insets.top + insets.bottom;
2085                return alloc;
2086            }
2087            return null;
2088        }
2089
2090        /**
2091         * Determines the bounding box of the character at the given
2092         * index into the string. The bounds are returned in local
2093         * coordinates. If the index is invalid a null rectangle
2094         * is returned.
2095         *
2096         * The screen coordinates returned are "unscrolled coordinates"
2097         * if the JTextComponent is contained in a JScrollPane in which
2098         * case the resulting rectangle should be composed with the parent
2099         * coordinates. A good algorithm to use is:
2100         * <nf>
2101         * Accessible a:
2102         * AccessibleText at = a.getAccessibleText();
2103         * AccessibleComponent ac = a.getAccessibleComponent();
2104         * Rectangle r = at.getCharacterBounds();
2105         * Point p = ac.getLocation();
2106         * r.x += p.x;
2107         * r.y += p.y;
2108         * </nf>
2109     *
2110     * Note: the JTextComponent must have a valid size (e.g. have
2111     * been added to a parent container whose ancestor container
2112     * is a valid top-level window) for this method to be able
2113     * to return a meaningful (non-null) value.
2114         *
2115         * @param i the index into the String >= 0
2116         * @return the screen coordinates of the character's bounding box
2117         */

2118        public Rectangle getCharacterBounds(int i) {
2119            if (i < 0 || i > model.getLength()-1) {
2120                return null;
2121            }
2122        TextUI ui = getUI();
2123        if (ui == null) {
2124        return null;
2125        }
2126        Rectangle rect = null;
2127        Rectangle alloc = getRootEditorRect();
2128        if (alloc == null) {
2129        return null;
2130        }
2131            if (model instanceof AbstractDocument JavaDoc) {
2132                ((AbstractDocument JavaDoc)model).readLock();
2133            }
2134            try {
2135            View JavaDoc rootView = ui.getRootView(JTextComponent.this);
2136            if (rootView != null) {
2137                rootView.setSize(alloc.width, alloc.height);
2138
2139                Shape bounds = rootView.modelToView(i,
2140                        Position.Bias.Forward, i+1,
2141                        Position.Bias.Backward, alloc);
2142
2143                rect = (bounds instanceof Rectangle) ?
2144                 (Rectangle)bounds : bounds.getBounds();
2145
2146        }
2147            } catch (BadLocationException JavaDoc e) {
2148            } finally {
2149                if (model instanceof AbstractDocument JavaDoc) {
2150                    ((AbstractDocument JavaDoc)model).readUnlock();
2151                }
2152            }
2153        return rect;
2154        }
2155
2156        /**
2157         * Returns the number of characters (valid indices)
2158         *
2159         * @return the number of characters >= 0
2160         */

2161        public int getCharCount() {
2162            return model.getLength();
2163        }
2164
2165        /**
2166         * Returns the zero-based offset of the caret.
2167         *
2168         * Note: The character to the right of the caret will have the
2169         * same index value as the offset (the caret is between
2170         * two characters).
2171         *
2172         * @return the zero-based offset of the caret.
2173         */

2174        public int getCaretPosition() {
2175            return JTextComponent.this.getCaretPosition();
2176        }
2177
2178        /**
2179         * Returns the AttributeSet for a given character (at a given index).
2180         *
2181         * @param i the zero-based index into the text
2182         * @return the AttributeSet of the character
2183         */

2184        public AttributeSet JavaDoc getCharacterAttribute(int i) {
2185            Element JavaDoc e = null;
2186            if (model instanceof AbstractDocument JavaDoc) {
2187                ((AbstractDocument JavaDoc)model).readLock();
2188            }
2189            try {
2190                for (e = model.getDefaultRootElement(); ! e.isLeaf(); ) {
2191                    int index = e.getElementIndex(i);
2192                    e = e.getElement(index);
2193                }
2194            } finally {
2195                if (model instanceof AbstractDocument JavaDoc) {
2196                    ((AbstractDocument JavaDoc)model).readUnlock();
2197                }
2198            }
2199            return e.getAttributes();
2200        }
2201
2202
2203        /**
2204         * Returns the start offset within the selected text.
2205         * If there is no selection, but there is
2206         * a caret, the start and end offsets will be the same.
2207         * Return 0 if the text is empty, or the caret position
2208         * if no selection.
2209         *
2210         * @return the index into the text of the start of the selection >= 0
2211         */

2212        public int getSelectionStart() {
2213            return JTextComponent.this.getSelectionStart();
2214        }
2215
2216        /**
2217         * Returns the end offset within the selected text.
2218         * If there is no selection, but there is
2219         * a caret, the start and end offsets will be the same.
2220         * Return 0 if the text is empty, or the caret position
2221         * if no selection.
2222         *
2223         * @return the index into teh text of the end of the selection >= 0
2224         */

2225        public int getSelectionEnd() {
2226            return JTextComponent.this.getSelectionEnd();
2227        }
2228
2229        /**
2230         * Returns the portion of the text that is selected.
2231         *
2232         * @return the text, null if no selection
2233         */

2234        public String JavaDoc getSelectedText() {
2235            return JTextComponent.this.getSelectedText();
2236        }
2237
2238       /**
2239         * IndexedSegment extends Segment adding the offset into the
2240         * the model the <code>Segment</code> was asked for.
2241         */

2242        private class IndexedSegment extends Segment JavaDoc {
2243            /**
2244             * Offset into the model that the position represents.
2245             */

2246            public int modelOffset;
2247        }
2248
2249
2250        // TIGER - 4170173
2251
/**
2252         * Returns the String at a given index. Whitespace
2253         * between words is treated as a word.
2254         *
2255         * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2256         * @param index an index within the text
2257         * @return the letter, word, or sentence.
2258         *
2259         */

2260        public String JavaDoc getAtIndex(int part, int index) {
2261            return getAtIndex(part, index, 0);
2262        }
2263
2264
2265        /**
2266         * Returns the String after a given index. Whitespace
2267         * between words is treated as a word.
2268         *
2269         * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2270         * @param index an index within the text
2271         * @return the letter, word, or sentence.
2272         */

2273        public String JavaDoc getAfterIndex(int part, int index) {
2274            return getAtIndex(part, index, 1);
2275        }
2276
2277
2278        /**
2279         * Returns the String before a given index. Whitespace
2280         * between words is treated a word.
2281         *
2282         * @param part the CHARACTER, WORD, or SENTENCE to retrieve
2283         * @param index an index within the text
2284         * @return the letter, word, or sentence.
2285         */

2286        public String JavaDoc getBeforeIndex(int part, int index) {
2287            return getAtIndex(part, index, -1);
2288        }
2289
2290
2291        /**
2292         * Gets the word, sentence, or character at <code>index</code>.
2293         * If <code>direction</code> is non-null this will find the
2294         * next/previous word/sentence/character.
2295         */

2296        private String JavaDoc getAtIndex(int part, int index, int direction) {
2297            if (model instanceof AbstractDocument JavaDoc) {
2298                ((AbstractDocument JavaDoc)model).readLock();
2299            }
2300            try {
2301                if (index < 0 || index >= model.getLength()) {
2302                    return null;
2303                }
2304                switch (part) {
2305                case AccessibleText.CHARACTER:
2306                    if (index + direction < model.getLength() &&
2307                        index + direction >= 0) {
2308                        return model.getText(index + direction, 1);
2309                    }
2310                    break;
2311
2312
2313                case AccessibleText.WORD:
2314                case AccessibleText.SENTENCE:
2315                    IndexedSegment seg = getSegmentAt(part, index);
2316                    if (seg != null) {
2317                        if (direction != 0) {
2318                            int next;
2319
2320
2321                            if (direction < 0) {
2322                                next = seg.modelOffset - 1;
2323                            }
2324                            else {
2325                                next = seg.modelOffset + direction * seg.count;
2326                            }
2327                            if (next >= 0 && next <= model.getLength()) {
2328                                seg = getSegmentAt(part, next);
2329                            }
2330                            else {
2331                                seg = null;
2332                            }
2333                        }
2334                        if (seg != null) {
2335                            return new String JavaDoc(seg.array, seg.offset,
2336                                                  seg.count);
2337                        }
2338                    }
2339                    break;
2340
2341
2342                default:
2343                    break;
2344                }
2345            } catch (BadLocationException JavaDoc e) {
2346            } finally {
2347                if (model instanceof AbstractDocument JavaDoc) {
2348                    ((AbstractDocument JavaDoc)model).readUnlock();
2349                }
2350            }
2351            return null;
2352        }
2353
2354
2355        /*
2356         * Returns the paragraph element for the specified index.
2357         */

2358        private Element JavaDoc getParagraphElement(int index) {
2359            if (model instanceof PlainDocument JavaDoc ) {
2360                PlainDocument JavaDoc sdoc = (PlainDocument JavaDoc)model;
2361                return sdoc.getParagraphElement(index);
2362            } else if (model instanceof StyledDocument JavaDoc) {
2363                StyledDocument JavaDoc sdoc = (StyledDocument JavaDoc)model;
2364                return sdoc.getParagraphElement(index);
2365            } else {
2366                Element JavaDoc para = null;
2367                for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) {
2368                    int pos = para.getElementIndex(index);
2369                    para = para.getElement(pos);
2370                }
2371                if (para == null) {
2372                    return null;
2373                }
2374                return para.getParentElement();
2375            }
2376        }
2377        
2378        /*
2379         * Returns a <code>Segment</code> containing the paragraph text
2380         * at <code>index</code>, or null if <code>index</code> isn't
2381         * valid.
2382         */

2383        private IndexedSegment getParagraphElementText(int index)
2384                                  throws BadLocationException JavaDoc {
2385            Element JavaDoc para = getParagraphElement(index);
2386
2387
2388            if (para != null) {
2389                IndexedSegment segment = new IndexedSegment();
2390                try {
2391                    int length = para.getEndOffset() - para.getStartOffset();
2392                    model.getText(para.getStartOffset(), length, segment);
2393                } catch (BadLocationException JavaDoc e) {
2394                    return null;
2395                }
2396                segment.modelOffset = para.getStartOffset();
2397                return segment;
2398            }
2399            return null;
2400        }
2401
2402
2403        /**
2404         * Returns the Segment at <code>index</code> representing either
2405         * the paragraph or sentence as identified by <code>part</code>, or
2406         * null if a valid paragraph/sentence can't be found. The offset
2407         * will point to the start of the word/sentence in the array, and
2408         * the modelOffset will point to the location of the word/sentence
2409         * in the model.
2410         */

2411        private IndexedSegment getSegmentAt(int part, int index) throws
2412                                  BadLocationException JavaDoc {
2413            IndexedSegment seg = getParagraphElementText(index);
2414            if (seg == null) {
2415                return null;
2416            }
2417            BreakIterator iterator;
2418            switch (part) {
2419            case AccessibleText.WORD:
2420                iterator = BreakIterator.getWordInstance(getLocale());
2421                break;
2422            case AccessibleText.SENTENCE:
2423                iterator = BreakIterator.getSentenceInstance(getLocale());
2424                break;
2425            default:
2426                return null;
2427            }
2428            seg.first();
2429            iterator.setText(seg);
2430            int end = iterator.following(index - seg.modelOffset + seg.offset);
2431            if (end == BreakIterator.DONE) {
2432                return null;
2433            }
2434            if (end > seg.offset + seg.count) {
2435                return null;
2436            }
2437            int begin = iterator.previous();
2438            if (begin == BreakIterator.DONE ||
2439                         begin >= seg.offset + seg.count) {
2440                return null;
2441            }
2442            seg.modelOffset = seg.modelOffset + begin - seg.offset;
2443            seg.offset = begin;
2444            seg.count = end - begin;
2445            return seg;
2446        }
2447 
2448    // begin AccessibleEditableText methods -----
2449

2450    /**
2451     * Returns the AccessibleEditableText interface for
2452     * this text component.
2453     *
2454     * @return the AccessibleEditableText interface
2455     */

2456    public AccessibleEditableText getAccessibleEditableText() {
2457        return this;
2458    }
2459    
2460    /**
2461     * Sets the text contents to the specified string.
2462     *
2463     * @param s the string to set the text contents
2464     */

2465    public void setTextContents(String JavaDoc s) {
2466        JTextComponent.this.setText(s);
2467    }
2468    
2469    /**
2470     * Inserts the specified string at the given index
2471     *
2472     * @param index the index in the text where the string will
2473     * be inserted
2474     * @param s the string to insert in the text
2475     */

2476    public void insertTextAtIndex(int index, String JavaDoc s) {
2477        Document JavaDoc doc = JTextComponent.this.getDocument();
2478        if (doc != null) {
2479        try {
2480            if (s != null && s.length() > 0) {
2481            boolean composedTextSaved = saveComposedText(index);
2482            doc.insertString(index, s, null);
2483            if (composedTextSaved) {
2484                restoreComposedText();
2485            }
2486            }
2487        } catch (BadLocationException JavaDoc e) {
2488                UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
2489        }
2490        }
2491    }
2492    
2493    /**
2494     * Returns the text string between two indices.
2495     *
2496     * @param startIndex the starting index in the text
2497     * @param endIndex the ending index in the text
2498     * @return the text string between the indices
2499     */

2500    public String JavaDoc getTextRange(int startIndex, int endIndex) {
2501        String JavaDoc txt = null;
2502        int p0 = Math.min(startIndex, endIndex);
2503        int p1 = Math.max(startIndex, endIndex);
2504        if (p0 != p1) {
2505        try {
2506            Document JavaDoc doc = JTextComponent.this.getDocument();
2507            txt = doc.getText(p0, p1 - p0);
2508        } catch (BadLocationException JavaDoc e) {
2509            throw new IllegalArgumentException JavaDoc(e.getMessage());
2510        }
2511        }
2512        return txt;
2513    }
2514    
2515    /**
2516     * Deletes the text between two indices
2517     *
2518     * @param startIndex the starting index in the text
2519     * @param endIndex the ending index in the text
2520     */

2521    public void delete(int startIndex, int endIndex) {
2522        if (isEditable() && isEnabled()) {
2523        try {
2524            int p0 = Math.min(startIndex, endIndex);
2525            int p1 = Math.max(startIndex, endIndex);
2526            if (p0 != p1) {
2527            Document JavaDoc doc = getDocument();
2528            doc.remove(p0, p1 - p0);
2529            }
2530        } catch (BadLocationException JavaDoc e) {
2531        }
2532        } else {
2533        UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
2534        }
2535    }
2536    
2537    /**
2538     * Cuts the text between two indices into the system clipboard.
2539     *
2540     * @param startIndex the starting index in the text
2541     * @param endIndex the ending index in the text
2542     */

2543    public void cut(int startIndex, int endIndex) {
2544        selectText(startIndex, endIndex);
2545        JTextComponent.this.cut();
2546    }
2547    
2548    /**
2549     * Pastes the text from the system clipboard into the text
2550     * starting at the specified index.
2551     *
2552     * @param startIndex the starting index in the text
2553     */

2554    public void paste(int startIndex) {
2555        setCaretPosition(startIndex);
2556        JTextComponent.this.paste();
2557    }
2558    
2559    /**
2560     * Replaces the text between two indices with the specified
2561     * string.
2562     *
2563     * @param startIndex the starting index in the text
2564     * @param endIndex the ending index in the text
2565     * @param s the string to replace the text between two indices
2566     */

2567    public void replaceText(int startIndex, int endIndex, String JavaDoc s) {
2568        selectText(startIndex, endIndex);
2569        JTextComponent.this.replaceSelection(s);
2570    }
2571    
2572    /**
2573     * Selects the text between two indices.
2574     *
2575     * @param startIndex the starting index in the text
2576     * @param endIndex the ending index in the text
2577     */

2578    public void selectText(int startIndex, int endIndex) {
2579        JTextComponent.this.select(startIndex, endIndex);
2580    }
2581
2582        /**
2583         * Sets attributes for the text between two indices.
2584         *
2585         * @param startIndex the starting index in the text
2586         * @param endIndex the ending index in the text
2587         * @param as the attribute set
2588         * @see AttributeSet
2589         */

2590        public void setAttributes(int startIndex, int endIndex,
2591            AttributeSet JavaDoc as) {
2592
2593            // Fixes bug 4487492
2594
Document JavaDoc doc = JTextComponent.this.getDocument();
2595            if (doc != null && doc instanceof StyledDocument JavaDoc) {
2596                StyledDocument JavaDoc sDoc = (StyledDocument JavaDoc)doc;
2597                int offset = startIndex;
2598                int length = endIndex - startIndex;
2599                sDoc.setCharacterAttributes(offset, length, as, true);
2600            }
2601        }
2602    
2603    // ----- end AccessibleEditableText methods
2604

2605
2606    // --- interface AccessibleAction methods ------------------------
2607

2608    public AccessibleAction getAccessibleAction() {
2609        return this;
2610    }
2611
2612    /**
2613     * Returns the number of accessible actions available in this object
2614     * If there are more than one, the first one is considered the
2615     * "default" action of the object.
2616     *
2617     * @return the zero-based number of Actions in this object
2618     */

2619    public int getAccessibleActionCount() {
2620        Action JavaDoc [] actions = JTextComponent.this.getActions();
2621        return actions.length;
2622    }
2623    
2624    /**
2625     * Returns a description of the specified action of the object.
2626     *
2627     * @param i zero-based index of the actions
2628     * @return a String description of the action
2629     * @see #getAccessibleActionCount
2630     */

2631    public String JavaDoc getAccessibleActionDescription(int i) {
2632        Action JavaDoc [] actions = JTextComponent.this.getActions();
2633        if (i < 0 || i >= actions.length) {
2634        return null;
2635        }
2636        return (String JavaDoc)actions[i].getValue(Action.NAME);
2637    }
2638    
2639    /**
2640     * Performs the specified Action on the object
2641     *
2642     * @param i zero-based index of actions
2643     * @return true if the action was performed; otherwise false.
2644     * @see #getAccessibleActionCount
2645     */

2646    public boolean doAccessibleAction(int i) {
2647        Action JavaDoc [] actions = JTextComponent.this.getActions();
2648        if (i < 0 || i >= actions.length) {
2649        return false;
2650        }
2651            ActionEvent ae =
2652                new ActionEvent(JTextComponent.this,
2653                                ActionEvent.ACTION_PERFORMED, null,
2654                                EventQueue.getMostRecentEventTime(),
2655                                getCurrentEventModifiers());
2656        actions[i].actionPerformed(ae);
2657        return true;
2658    }
2659    
2660    // ----- end AccessibleAction methods
2661

2662
2663    }
2664
2665
2666    // --- serialization ---------------------------------------------
2667

2668    private void readObject(ObjectInputStream s)
2669    throws IOException, ClassNotFoundException JavaDoc
2670    {
2671        s.defaultReadObject();
2672        caretEvent = new MutableCaretEvent(this);
2673        addMouseListener(caretEvent);
2674        addFocusListener(caretEvent);
2675    }
2676
2677    // --- member variables ----------------------------------
2678

2679    /**
2680     * The document model.
2681     */

2682    private Document JavaDoc model;
2683
2684    /**
2685     * The caret used to display the insert position
2686     * and navigate throughout the document.
2687     *
2688     * PENDING(prinz)
2689     * This should be serializable, default installed
2690     * by UI.
2691     */

2692    private transient Caret JavaDoc caret;
2693
2694    /**
2695     * Object responsible for restricting the cursor navigation.
2696     */

2697    private NavigationFilter JavaDoc navigationFilter;
2698
2699    /**
2700     * The object responsible for managing highlights.
2701     *
2702     * PENDING(prinz)
2703     * This should be serializable, default installed
2704     * by UI.
2705     */

2706    private transient Highlighter JavaDoc highlighter;
2707
2708    /**
2709     * The current key bindings in effect.
2710     *
2711     * PENDING(prinz)
2712     * This should be serializable, default installed
2713     * by UI.
2714     */

2715    private transient Keymap JavaDoc keymap;
2716
2717    private transient MutableCaretEvent caretEvent;
2718    private Color caretColor;
2719    private Color selectionColor;
2720    private Color selectedTextColor;
2721    private Color disabledTextColor;
2722    private boolean editable;
2723    private Insets margin;
2724    private char focusAccelerator;
2725    private boolean dragEnabled;
2726
2727    /**
2728     * TransferHandler used if one hasn't been supplied by the UI.
2729     */

2730    private static DefaultTransferHandler defaultTransferHandler;
2731
2732    /**
2733     * Maps from class name to Boolean indicating if
2734     * <code>processInputMethodEvent</code> has been overriden.
2735     */

2736    private static Map JavaDoc overrideMap;
2737
2738    /**
2739     * Returns a string representation of this <code>JTextComponent</code>.
2740     * This method is intended to be used only for debugging purposes, and the
2741     * content and format of the returned string may vary between
2742     * implementations. The returned string may be empty but may not
2743     * be <code>null</code>.
2744     * <P>
2745     * Overriding <code>paramString</code> to provide information about the
2746     * specific new aspects of the JFC components.
2747     *
2748     * @return a string representation of this <code>JTextComponent</code>
2749     */

2750    protected String JavaDoc paramString() {
2751        String JavaDoc editableString = (editable ?
2752                 "true" : "false");
2753        String JavaDoc caretColorString = (caretColor != null ?
2754                   caretColor.toString() : "");
2755        String JavaDoc selectionColorString = (selectionColor != null ?
2756                       selectionColor.toString() : "");
2757        String JavaDoc selectedTextColorString = (selectedTextColor != null ?
2758                      selectedTextColor.toString() : "");
2759        String JavaDoc disabledTextColorString = (disabledTextColor != null ?
2760                      disabledTextColor.toString() : "");
2761        String JavaDoc marginString = (margin != null ?
2762                   margin.toString() : "");
2763
2764        return super.paramString() +
2765        ",caretColor=" + caretColorString +
2766        ",disabledTextColor=" + disabledTextColorString +
2767        ",editable=" + editableString +
2768        ",margin=" + marginString +
2769        ",selectedTextColor=" + selectedTextColorString +
2770        ",selectionColor=" + selectionColorString;
2771    }
2772
2773
2774    /**
2775     * A Simple TransferHandler that exports the data as a String, and
2776     * imports the data from the String clipboard. This is only used
2777     * if the UI hasn't supplied one, which would only happen if someone
2778     * hasn't subclassed Basic.
2779     */

2780    static class DefaultTransferHandler extends TransferHandler implements
2781                                        UIResource {
2782        public void exportToClipboard(JComponent comp, Clipboard clipboard,
2783                                      int action) throws IllegalStateException JavaDoc {
2784            if (comp instanceof JTextComponent JavaDoc) {
2785                JTextComponent JavaDoc text = (JTextComponent JavaDoc)comp;
2786                int p0 = text.getSelectionStart();
2787                int p1 = text.getSelectionEnd();
2788                if (p0 != p1) {
2789                    try {
2790                        Document JavaDoc doc = text.getDocument();
2791                        String JavaDoc srcData = doc.getText(p0, p1 - p0);
2792                        StringSelection contents =new StringSelection(srcData);
2793
2794                        // this may throw an IllegalStateException,
2795
// but it will be caught and handled in the
2796
// action that invoked this method
2797
clipboard.setContents(contents, null);
2798
2799                        if (action == TransferHandler.MOVE) {
2800                            doc.remove(p0, p1 - p0);
2801                        }
2802                    } catch (BadLocationException JavaDoc ble) {}
2803                }
2804            }
2805        }
2806        public boolean importData(JComponent comp, Transferable t) {
2807            if (comp instanceof JTextComponent JavaDoc) {
2808                DataFlavor flavor = getFlavor(t.getTransferDataFlavors());
2809
2810                if (flavor != null) {
2811            InputContext JavaDoc ic = comp.getInputContext();
2812            if (ic != null) {
2813            ic.endComposition();
2814            }
2815                    try {
2816                        String JavaDoc data = (String JavaDoc)t.getTransferData(flavor);
2817
2818                        ((JTextComponent JavaDoc)comp).replaceSelection(data);
2819                        return true;
2820                    } catch (UnsupportedFlavorException ufe) {
2821                    } catch (IOException ioe) {
2822                    }
2823                }
2824            }
2825            return false;
2826        }
2827        public boolean canImport(JComponent comp,
2828                                 DataFlavor[] transferFlavors) {
2829            JTextComponent JavaDoc c = (JTextComponent JavaDoc)comp;
2830            if (!(c.isEditable() && c.isEnabled())) {
2831                return false;
2832            }
2833            return (getFlavor(transferFlavors) != null);
2834        }
2835        public int getSourceActions(JComponent c) {
2836            return NONE;
2837        }
2838        private DataFlavor getFlavor(DataFlavor[] flavors) {
2839            if (flavors != null) {
2840                for (int counter = 0; counter < flavors.length; counter++) {
2841                    if (flavors[counter].equals(DataFlavor.stringFlavor)) {
2842                        return flavors[counter];
2843                    }
2844                }
2845            }
2846            return null;
2847        }
2848    }
2849
2850    /**
2851     * Returns the JTextComponent that most recently had focus. The returned
2852     * value may currently have focus.
2853     */

2854    static final JTextComponent JavaDoc getFocusedComponent() {
2855        return (JTextComponent JavaDoc)AppContext.getAppContext().
2856            get(FOCUSED_COMPONENT);
2857    }
2858
2859    private int getCurrentEventModifiers() {
2860        int modifiers = 0;
2861        AWTEvent currentEvent = EventQueue.getCurrentEvent();
2862        if (currentEvent instanceof InputEvent) {
2863            modifiers = ((InputEvent)currentEvent).getModifiers();
2864        } else if (currentEvent instanceof ActionEvent) {
2865            modifiers = ((ActionEvent)currentEvent).getModifiers();
2866        }
2867        return modifiers;
2868    }
2869
2870    private static final Object JavaDoc KEYMAP_TABLE =
2871        new StringBuilder JavaDoc("JTextComponent_KeymapTable");
2872    private JTextComponent JavaDoc editor;
2873    //
2874
// member variables used for on-the-spot input method
2875
// editing style support
2876
//
2877
private transient InputMethodRequests JavaDoc inputMethodRequestsHandler;
2878    private SimpleAttributeSet JavaDoc composedTextAttribute;
2879    private String JavaDoc composedTextContent;
2880    private Position JavaDoc composedTextStart;
2881    private Position JavaDoc composedTextEnd;
2882    private Position JavaDoc latestCommittedTextStart;
2883    private Position JavaDoc latestCommittedTextEnd;
2884    private ComposedTextCaret composedTextCaret;
2885    private transient Caret JavaDoc originalCaret;
2886    /**
2887     * Set to true after the check for the override of processInputMethodEvent
2888     * has been checked.
2889     */

2890    private boolean checkedInputOverride;
2891    private boolean needToSendKeyTypedEvent;
2892
2893    static class DefaultKeymap implements Keymap JavaDoc {
2894
2895        DefaultKeymap(String JavaDoc nm, Keymap JavaDoc parent) {
2896            this.nm = nm;
2897            this.parent = parent;
2898            bindings = new Hashtable JavaDoc();
2899        }
2900
2901        /**
2902         * Fetch the default action to fire if a
2903         * key is typed (ie a KEY_TYPED KeyEvent is received)
2904         * and there is no binding for it. Typically this
2905         * would be some action that inserts text so that
2906         * the keymap doesn't require an action for each
2907         * possible key.
2908         */

2909        public Action JavaDoc getDefaultAction() {
2910            if (defaultAction != null) {
2911                return defaultAction;
2912            }
2913            return (parent != null) ? parent.getDefaultAction() : null;
2914        }
2915
2916        /**
2917         * Set the default action to fire if a key is typed.
2918         */

2919        public void setDefaultAction(Action JavaDoc a) {
2920            defaultAction = a;
2921        }
2922
2923        public String JavaDoc getName() {
2924            return nm;
2925        }
2926
2927        public Action JavaDoc getAction(KeyStroke key) {
2928            Action JavaDoc a = (Action JavaDoc) bindings.get(key);
2929            if ((a == null) && (parent != null)) {
2930                a = parent.getAction(key);
2931            }
2932            return a;
2933        }
2934
2935        public KeyStroke[] getBoundKeyStrokes() {
2936            KeyStroke[] keys = new KeyStroke[bindings.size()];
2937            int i = 0;
2938            for (Enumeration JavaDoc e = bindings.keys() ; e.hasMoreElements() ;) {
2939                keys[i++] = (KeyStroke) e.nextElement();
2940            }
2941            return keys;
2942        }
2943
2944        public Action JavaDoc[] getBoundActions() {
2945            Action JavaDoc[] actions = new Action JavaDoc[bindings.size()];
2946            int i = 0;
2947            for (Enumeration JavaDoc e = bindings.elements() ; e.hasMoreElements() ;) {
2948                actions[i++] = (Action JavaDoc) e.nextElement();
2949            }
2950            return actions;
2951        }
2952
2953        public KeyStroke[] getKeyStrokesForAction(Action JavaDoc a) {
2954        if (a == null) {
2955        return null;
2956        }
2957        KeyStroke[] retValue = null;
2958        // Determine local bindings first.
2959
Vector JavaDoc keyStrokes = null;
2960        for (Enumeration JavaDoc enum_ = bindings.keys();
2961         enum_.hasMoreElements();) {
2962        Object JavaDoc key = enum_.nextElement();
2963        if (bindings.get(key) == a) {
2964            if (keyStrokes == null) {
2965            keyStrokes = new Vector JavaDoc();
2966            }
2967            keyStrokes.addElement(key);
2968        }
2969        }
2970        // See if the parent has any.
2971
if (parent != null) {
2972        KeyStroke[] pStrokes = parent.getKeyStrokesForAction(a);
2973        if (pStrokes != null) {
2974            // Remove any bindings defined in the parent that
2975
// are locally defined.
2976
int rCount = 0;
2977            for (int counter = pStrokes.length - 1; counter >= 0;
2978             counter--) {
2979            if (isLocallyDefined(pStrokes[counter])) {
2980                pStrokes[counter] = null;
2981                rCount++;
2982            }
2983            }
2984            if (rCount > 0 && rCount < pStrokes.length) {
2985            if (keyStrokes == null) {
2986                keyStrokes = new Vector JavaDoc();
2987            }
2988            for (int counter = pStrokes.length - 1; counter >= 0;
2989                 counter--) {
2990                if (pStrokes[counter] != null) {
2991                keyStrokes.addElement(pStrokes[counter]);
2992                }
2993            }
2994            }
2995            else if (rCount == 0) {
2996            if (keyStrokes == null) {
2997                retValue = pStrokes;
2998            }
2999            else {
3000                retValue = new KeyStroke[keyStrokes.size() +
3001                            pStrokes.length];
3002                keyStrokes.copyInto(retValue);
3003                System.arraycopy(pStrokes, 0, retValue,
3004                    keyStrokes.size(), pStrokes.length);
3005                keyStrokes = null;
3006            }
3007            }
3008        }
3009        }
3010        if (keyStrokes != null) {
3011        retValue = new KeyStroke[keyStrokes.size()];
3012        keyStrokes.copyInto(retValue);
3013        }
3014            return retValue;
3015        }
3016
3017        public boolean isLocallyDefined(KeyStroke key) {
3018            return bindings.containsKey(key);
3019        }
3020
3021        public void addActionForKeyStroke(KeyStroke key, Action JavaDoc a) {
3022            bindings.put(key, a);
3023        }
3024
3025        public void removeKeyStrokeBinding(KeyStroke key) {
3026            bindings.remove(key);
3027        }
3028
3029        public void removeBindings() {
3030            bindings.clear();
3031        }
3032
3033        public Keymap JavaDoc getResolveParent() {
3034            return parent;
3035        }
3036
3037        public void setResolveParent(Keymap JavaDoc parent) {
3038            this.parent = parent;
3039        }
3040
3041        /**
3042         * String representation of the keymap... potentially
3043         * a very long string.
3044         */

3045        public String JavaDoc toString() {
3046            return "Keymap[" + nm + "]" + bindings;
3047        }
3048
3049        String JavaDoc nm;
3050        Keymap JavaDoc parent;
3051        Hashtable JavaDoc bindings;
3052        Action JavaDoc defaultAction;
3053    }
3054
3055
3056    /**
3057     * KeymapWrapper wraps a Keymap inside an InputMap. For KeymapWrapper
3058     * to be useful it must be used with a KeymapActionMap.
3059     * KeymapWrapper for the most part, is an InputMap with two parents.
3060     * The first parent visited is ALWAYS the Keymap, with the second
3061     * parent being the parent inherited from InputMap. If
3062     * <code>keymap.getAction</code> returns null, implying the Keymap
3063     * does not have a binding for the KeyStroke,
3064     * the parent is then visited. If the Keymap has a binding, the
3065     * Action is returned, if not and the KeyStroke represents a
3066     * KeyTyped event and the Keymap has a defaultAction,
3067     * <code>DefaultActionKey</code> is returned.
3068     * <p>KeymapActionMap is then able to transate the object passed in
3069     * to either message the Keymap, or message its default implementation.
3070     */

3071    static class KeymapWrapper extends InputMap {
3072    static final Object JavaDoc DefaultActionKey = new Object JavaDoc();
3073
3074    private Keymap JavaDoc keymap;
3075
3076    KeymapWrapper(Keymap JavaDoc keymap) {
3077        this.keymap = keymap;
3078    }
3079
3080    public KeyStroke[] keys() {
3081        KeyStroke[] sKeys = super.keys();
3082        KeyStroke[] keymapKeys = keymap.getBoundKeyStrokes();
3083        int sCount = (sKeys == null) ? 0 : sKeys.length;
3084        int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
3085        if (sCount == 0) {
3086        return keymapKeys;
3087        }
3088        if (keymapCount == 0) {
3089        return sKeys;
3090        }
3091        KeyStroke[] retValue = new KeyStroke[sCount + keymapCount];
3092        // There may be some duplication here...
3093
System.arraycopy(sKeys, 0, retValue, 0, sCount);
3094        System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
3095        return retValue;
3096    }
3097
3098    public int size() {
3099        // There may be some duplication here...
3100
KeyStroke[] keymapStrokes = keymap.getBoundKeyStrokes();
3101        int keymapCount = (keymapStrokes == null) ? 0:
3102                       keymapStrokes.length;
3103        return super.size() + keymapCount;
3104    }
3105
3106    public Object JavaDoc get(KeyStroke keyStroke) {
3107        Object JavaDoc retValue = keymap.getAction(keyStroke);
3108            if (retValue == null) {
3109        retValue = super.get(keyStroke);
3110        if (retValue == null &&
3111            keyStroke.getKeyChar() != KeyEvent.CHAR_UNDEFINED &&
3112            keymap.getDefaultAction() != null) {
3113            // Implies this is a KeyTyped event, use the default
3114
// action.
3115
retValue = DefaultActionKey;
3116        }
3117        }
3118        return retValue;
3119    }
3120    }
3121
3122
3123    /**
3124     * Wraps a Keymap inside an ActionMap. This is used with
3125     * a KeymapWrapper. If <code>get</code> is passed in
3126     * <code>KeymapWrapper.DefaultActionKey</code>, the default action is
3127     * returned, otherwise if the key is an Action, it is returned.
3128     */

3129    static class KeymapActionMap extends ActionMap {
3130    private Keymap JavaDoc keymap;
3131
3132    KeymapActionMap(Keymap JavaDoc keymap) {
3133        this.keymap = keymap;
3134    }
3135
3136    public Object JavaDoc[] keys() {
3137        Object JavaDoc[] sKeys = super.keys();
3138        Object JavaDoc[] keymapKeys = keymap.getBoundActions();
3139        int sCount = (sKeys == null) ? 0 : sKeys.length;
3140        int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
3141        boolean hasDefault = (keymap.getDefaultAction() != null);
3142        if (hasDefault) {
3143        keymapCount++;
3144        }
3145        if (sCount == 0) {
3146        if (hasDefault) {
3147            Object JavaDoc[] retValue = new Object JavaDoc[keymapCount];
3148            if (keymapCount > 1) {
3149            System.arraycopy(keymapKeys, 0, retValue, 0,
3150                     keymapCount - 1);
3151            }
3152            retValue[keymapCount - 1] = KeymapWrapper.DefaultActionKey;
3153            return retValue;
3154        }
3155        return keymapKeys;
3156        }
3157        if (keymapCount == 0) {
3158        return sKeys;
3159        }
3160        Object JavaDoc[] retValue = new Object JavaDoc[sCount + keymapCount];
3161        // There may be some duplication here...
3162
System.arraycopy(sKeys, 0, retValue, 0, sCount);
3163        if (hasDefault) {
3164        if (keymapCount > 1) {
3165            System.arraycopy(keymapKeys, 0, retValue, sCount,
3166                     keymapCount - 1);
3167        }
3168        retValue[sCount + keymapCount - 1] = KeymapWrapper.
3169                                         DefaultActionKey;
3170        }
3171        else {
3172        System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
3173        }
3174        return retValue;
3175    }
3176
3177    public int size() {
3178        // There may be some duplication here...
3179
Object JavaDoc[] actions = keymap.getBoundActions();
3180        int keymapCount = (actions == null) ? 0 : actions.length;
3181        if (keymap.getDefaultAction() != null) {
3182        keymapCount++;
3183        }
3184        return super.size() + keymapCount;
3185    }
3186
3187    public Action JavaDoc get(Object JavaDoc key) {
3188        Action JavaDoc retValue = super.get(key);
3189        if (retValue == null) {
3190        // Try the Keymap.
3191
if (key == KeymapWrapper.DefaultActionKey) {
3192            retValue = keymap.getDefaultAction();
3193        }
3194        else if (key instanceof Action JavaDoc) {
3195            // This is a little iffy, technically an Action is
3196
// a valid Key. We're assuming the Action came from
3197
// the InputMap though.
3198
retValue = (Action JavaDoc)key;
3199        }
3200        }
3201        return retValue;
3202    }
3203    }
3204
3205    private static final Object JavaDoc FOCUSED_COMPONENT =
3206        new StringBuilder JavaDoc("JTextComponent_FocusedComponent");
3207    
3208    /**
3209     * The default keymap that will be shared by all
3210     * <code>JTextComponent</code> instances unless they
3211     * have had a different keymap set.
3212     */

3213    public static final String JavaDoc DEFAULT_KEYMAP = "default";
3214
3215    /**
3216     * Event to use when firing a notification of change to caret
3217     * position. This is mutable so that the event can be reused
3218     * since caret events can be fairly high in bandwidth.
3219     */

3220    static class MutableCaretEvent extends CaretEvent implements ChangeListener, FocusListener, MouseListener {
3221
3222        MutableCaretEvent(JTextComponent JavaDoc c) {
3223            super(c);
3224        }
3225
3226        final void fire() {
3227            JTextComponent JavaDoc c = (JTextComponent JavaDoc) getSource();
3228            if (c != null) {
3229                Caret JavaDoc caret = c.getCaret();
3230                dot = caret.getDot();
3231                mark = caret.getMark();
3232                c.fireCaretUpdate(this);
3233            }
3234        }
3235
3236        public final String JavaDoc toString() {
3237            return "dot=" + dot + "," + "mark=" + mark;
3238        }
3239
3240        // --- CaretEvent methods -----------------------
3241

3242        public final int getDot() {
3243            return dot;
3244        }
3245
3246        public final int getMark() {
3247            return mark;
3248        }
3249
3250        // --- ChangeListener methods -------------------
3251

3252        public final void stateChanged(ChangeEvent e) {
3253            if (! dragActive) {
3254                fire();
3255            }
3256        }
3257
3258        // --- FocusListener methods -----------------------------------
3259
public void focusGained(FocusEvent fe) {
3260            AppContext.getAppContext().put(FOCUSED_COMPONENT,
3261                                           fe.getSource());
3262        }
3263
3264        public void focusLost(FocusEvent fe) {
3265        }
3266
3267        // --- MouseListener methods -----------------------------------
3268

3269        /**
3270         * Requests focus on the associated
3271         * text component, and try to set the cursor position.
3272         *
3273         * @param e the mouse event
3274         * @see MouseListener#mousePressed
3275         */

3276        public final void mousePressed(MouseEvent e) {
3277            dragActive = true;
3278        }
3279
3280        /**
3281         * Called when the mouse is released.
3282         *
3283         * @param e the mouse event
3284         * @see MouseListener#mouseReleased
3285         */

3286        public final void mouseReleased(MouseEvent e) {
3287            dragActive = false;
3288            fire();
3289        }
3290
3291        public final void mouseClicked(MouseEvent e) {
3292        }
3293
3294        public final void mouseEntered(MouseEvent e) {
3295        }
3296
3297        public final void mouseExited(MouseEvent e) {
3298        }
3299
3300        private boolean dragActive;
3301        private int dot;
3302        private int mark;
3303    }
3304
3305    //
3306
// Process any input method events that the component itself
3307
// recognizes. The default on-the-spot handling for input method
3308
// composed(uncommitted) text is done here after all input
3309
// method listeners get called for stealing the events.
3310
//
3311
protected void processInputMethodEvent(InputMethodEvent e) {
3312    // let listeners handle the events
3313
super.processInputMethodEvent(e);
3314
3315    if (!e.isConsumed()) {
3316            if (! isEditable()) {
3317                return;
3318            } else {
3319            switch (e.getID()) {
3320        case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED:
3321            replaceInputMethodText(e);
3322
3323            // fall through
3324

3325        case InputMethodEvent.CARET_POSITION_CHANGED:
3326            setInputMethodCaretPosition(e);
3327            break;
3328        }
3329        }
3330
3331        e.consume();
3332    }
3333    }
3334
3335    //
3336
// Overrides this method to become an active input method client.
3337
//
3338
public InputMethodRequests JavaDoc getInputMethodRequests() {
3339        if (inputMethodRequestsHandler == null) {
3340        inputMethodRequestsHandler =
3341            (InputMethodRequests JavaDoc)new InputMethodRequestsHandler();
3342        Document JavaDoc doc = getDocument();
3343        if (doc != null) {
3344        doc.addDocumentListener((DocumentListener)inputMethodRequestsHandler);
3345        }
3346    }
3347
3348    return inputMethodRequestsHandler;
3349    }
3350
3351    //
3352
// Overrides this method to watch the listener installed.
3353
//
3354
public void addInputMethodListener(InputMethodListener l) {
3355        super.addInputMethodListener(l);
3356    if (l != null) {
3357        needToSendKeyTypedEvent = false;
3358            checkedInputOverride = true;
3359    }
3360    }
3361    
3362
3363    //
3364
// Default implementation of the InputMethodRequests interface.
3365
//
3366
class InputMethodRequestsHandler implements InputMethodRequests JavaDoc, DocumentListener {
3367
3368    // --- InputMethodRequests methods ---
3369

3370    public AttributedCharacterIterator cancelLatestCommittedText(
3371                        Attribute JavaDoc[] attributes) {
3372        Document JavaDoc doc = getDocument();
3373        if ((doc != null) && (latestCommittedTextStart != null)
3374                && (!latestCommittedTextStart.equals(latestCommittedTextEnd))) {
3375        try {
3376            int startIndex = latestCommittedTextStart.getOffset();
3377                    int endIndex = latestCommittedTextEnd.getOffset();
3378            String JavaDoc latestCommittedText =
3379                        doc.getText(startIndex, endIndex - startIndex);
3380            doc.remove(startIndex, endIndex - startIndex);
3381            return new AttributedString(latestCommittedText).getIterator();
3382        } catch (BadLocationException JavaDoc ble) {}
3383        }
3384        return null;
3385        }
3386
3387        public AttributedCharacterIterator getCommittedText(int beginIndex,
3388                    int endIndex, Attribute JavaDoc[] attributes) {
3389        int composedStartIndex = 0;
3390        int composedEndIndex = 0;
3391        if (composedTextExists()) {
3392        composedStartIndex = composedTextStart.getOffset();
3393        composedEndIndex = composedTextEnd.getOffset();
3394        }
3395
3396        String JavaDoc committed;
3397        try {
3398        if (beginIndex < composedStartIndex) {
3399            if (endIndex <= composedStartIndex) {
3400            committed = getText(beginIndex, endIndex - beginIndex);
3401            } else {
3402            int firstPartLength = composedStartIndex - beginIndex;
3403            committed = getText(beginIndex, firstPartLength) +
3404                getText(composedEndIndex, endIndex - beginIndex - firstPartLength);
3405            }
3406        } else {
3407            committed = getText(beginIndex + (composedEndIndex - composedStartIndex),
3408                                endIndex - beginIndex);
3409        }
3410        } catch (BadLocationException JavaDoc ble) {
3411        throw new IllegalArgumentException JavaDoc("Invalid range");
3412        }
3413        return new AttributedString(committed).getIterator();
3414        }
3415
3416        public int getCommittedTextLength() {
3417        Document JavaDoc doc = getDocument();
3418        int length = 0;
3419        if (doc != null) {
3420        length = doc.getLength();
3421        if (composedTextContent != null) {
3422                    if (composedTextEnd == null
3423                          || composedTextStart == null) {
3424                        /*
3425                         * fix for : 6355666
3426                         * this is the case when this method is invoked
3427                         * from DocumentListener. At this point
3428                         * composedTextEnd and composedTextStart are
3429                         * not defined yet.
3430                         */

3431                        length -= composedTextContent.length();
3432                    } else {
3433                        length -= composedTextEnd.getOffset() -
3434                            composedTextStart.getOffset();
3435                    }
3436        }
3437            }
3438        return length;
3439        }
3440
3441        public int getInsertPositionOffset() {
3442        int composedStartIndex = 0;
3443        int composedEndIndex = 0;
3444        if (composedTextExists()) {
3445        composedStartIndex = composedTextStart.getOffset();
3446        composedEndIndex = composedTextEnd.getOffset();
3447        }
3448        int caretIndex = getCaretPosition();
3449
3450        if (caretIndex < composedStartIndex) {
3451        return caretIndex;
3452        } else if (caretIndex < composedEndIndex) {
3453        return composedStartIndex;
3454        } else {
3455        return caretIndex - (composedEndIndex - composedStartIndex);
3456        }
3457        }
3458
3459        public TextHitInfo JavaDoc getLocationOffset(int x, int y) {
3460        if (composedTextAttribute == null) {
3461            return null;
3462        } else {
3463            Point p = getLocationOnScreen();
3464            p.x = x - p.x;
3465            p.y = y - p.y;
3466            int pos = viewToModel(p);
3467            if ((pos >= composedTextStart.getOffset()) &&
3468            (pos <= composedTextEnd.getOffset())) {
3469                return TextHitInfo.leading(pos - composedTextStart.getOffset());
3470            } else {
3471                return null;
3472            }
3473        }
3474        }
3475
3476        public Rectangle getTextLocation(TextHitInfo JavaDoc offset) {
3477        Rectangle r;
3478        
3479        try {
3480            r = modelToView(getCaretPosition());
3481        if (r != null) {
3482            Point p = getLocationOnScreen();
3483            r.translate(p.x, p.y);
3484        }
3485        } catch (BadLocationException JavaDoc ble) {
3486            r = null;
3487        }
3488
3489        if (r == null)
3490            r = new Rectangle();
3491
3492        return r;
3493    }
3494
3495        public AttributedCharacterIterator getSelectedText(
3496                        Attribute JavaDoc[] attributes) {
3497        String JavaDoc selection = JTextComponent.this.getSelectedText();
3498        if (selection != null) {
3499            return new AttributedString(selection).getIterator();
3500        } else {
3501            return null;
3502        }
3503        }
3504
3505    // --- DocumentListener methods ---
3506

3507    public void changedUpdate(DocumentEvent e) {
3508        latestCommittedTextStart = latestCommittedTextEnd = null;
3509    }
3510
3511    public void insertUpdate(DocumentEvent e) {
3512        latestCommittedTextStart = latestCommittedTextEnd = null;
3513    }
3514
3515    public void removeUpdate(DocumentEvent e) {
3516        latestCommittedTextStart = latestCommittedTextEnd = null;
3517    }
3518    }
3519
3520    //
3521
// Replaces the current input method (composed) text according to
3522
// the passed input method event. This method also inserts the
3523
// committed text into the document.
3524
//
3525
private void replaceInputMethodText(InputMethodEvent e) {
3526        int commitCount = e.getCommittedCharacterCount();
3527    AttributedCharacterIterator text = e.getText();
3528    int composedTextIndex;
3529
3530    // old composed text deletion
3531
Document JavaDoc doc = getDocument();
3532    if (composedTextExists()) {
3533        try {
3534            doc.remove(composedTextStart.getOffset(),
3535                           composedTextEnd.getOffset() -
3536                           composedTextStart.getOffset());
3537        } catch (BadLocationException JavaDoc ble) {}
3538        composedTextStart = composedTextEnd = null;
3539        composedTextAttribute = null;
3540        composedTextContent = null;
3541    }
3542    
3543    if (text != null) {
3544        text.first();
3545        int committedTextStartIndex = 0;
3546        int committedTextEndIndex = 0;
3547
3548        // committed text insertion
3549
if (commitCount > 0) {
3550        // Remember latest committed text start index
3551
committedTextStartIndex = caret.getDot();
3552
3553        // Need to generate KeyTyped events for the committed text for components
3554
// that are not aware they are active input method clients.
3555
if (shouldSynthensizeKeyEvents()) {
3556            for (char c = text.current(); commitCount > 0;
3557                 c = text.next(), commitCount--) {
3558            KeyEvent ke = new KeyEvent(this, KeyEvent.KEY_TYPED,
3559                           EventQueue.getMostRecentEventTime(),
3560                           0, KeyEvent.VK_UNDEFINED, c);
3561            processKeyEvent(ke);
3562            }
3563        } else {
3564            StringBuffer JavaDoc strBuf = new StringBuffer JavaDoc();
3565            for (char c = text.current(); commitCount > 0;
3566                 c = text.next(), commitCount--) {
3567            strBuf.append(c);
3568            }
3569        
3570            // map it to an ActionEvent
3571
mapCommittedTextToAction(new String JavaDoc(strBuf));
3572        }
3573        
3574        // Remember latest committed text end index
3575
committedTextEndIndex = caret.getDot();
3576        }
3577
3578        // new composed text insertion
3579
composedTextIndex = text.getIndex();
3580        if (composedTextIndex < text.getEndIndex()) {
3581        createComposedTextAttribute(composedTextIndex, text);
3582        try {
3583            replaceSelection(null);
3584            doc.insertString(caret.getDot(), composedTextContent,
3585                        composedTextAttribute);
3586            composedTextStart = doc.createPosition(caret.getDot() -
3587                        composedTextContent.length());
3588                    composedTextEnd = doc.createPosition(caret.getDot());
3589        } catch (BadLocationException JavaDoc ble) {
3590                composedTextStart = composedTextEnd = null;
3591                composedTextAttribute = null;
3592                composedTextContent = null;
3593        }
3594        }
3595
3596        // Save the latest committed text information
3597
if (committedTextStartIndex != committedTextEndIndex) {
3598        try {
3599            latestCommittedTextStart = doc.
3600                        createPosition(committedTextStartIndex);
3601                    latestCommittedTextEnd = doc.
3602                        createPosition(committedTextEndIndex);
3603        } catch (BadLocationException JavaDoc ble) {
3604            latestCommittedTextStart =
3605                        latestCommittedTextEnd = null;
3606        }
3607        } else {
3608        latestCommittedTextStart =
3609                    latestCommittedTextEnd = null;
3610        }
3611    }
3612    }
3613
3614    private void createComposedTextAttribute(int composedIndex,
3615                        AttributedCharacterIterator text) {
3616    Document JavaDoc doc = getDocument();
3617        StringBuffer JavaDoc strBuf = new StringBuffer JavaDoc();
3618
3619    // create attributed string with no attributes
3620
for (char c = text.setIndex(composedIndex);
3621             c != CharacterIterator.DONE; c = text.next()) {
3622        strBuf.append(c);
3623    }
3624    
3625    composedTextContent = new String JavaDoc(strBuf);
3626    composedTextAttribute = new SimpleAttributeSet JavaDoc();
3627    composedTextAttribute.addAttribute(StyleConstants.ComposedTextAttribute,
3628        new AttributedString(text, composedIndex, text.getEndIndex()));
3629    }
3630
3631    private boolean saveComposedText(int pos) {
3632    if (composedTextExists()) {
3633        int start = composedTextStart.getOffset();
3634        int len = composedTextEnd.getOffset() -
3635                composedTextStart.getOffset();
3636        if (pos >= start && pos <= start + len) {
3637        try {
3638            getDocument().remove(start, len);
3639            return true;
3640        } catch (BadLocationException JavaDoc ble) {}
3641        }
3642    }
3643    return false;
3644    }
3645
3646    private void restoreComposedText() {
3647    Document JavaDoc doc = getDocument();
3648    try {
3649        doc.insertString(caret.getDot(),
3650                             composedTextContent,
3651                     composedTextAttribute);
3652        composedTextStart = doc.createPosition(caret.getDot() -
3653                composedTextContent.length());
3654            composedTextEnd = doc.createPosition(caret.getDot());
3655    } catch (BadLocationException JavaDoc ble) {}
3656    }
3657    
3658    //
3659
// Map committed text to an ActionEvent. If the committed text length is 1,
3660
// treat it as a KeyStroke, otherwise or there is no KeyStroke defined,
3661
// treat it just as a default action.
3662
//
3663
private void mapCommittedTextToAction(String JavaDoc committedText) {
3664        Keymap JavaDoc binding = getKeymap();
3665        if (binding != null) {
3666            Action JavaDoc a = null;
3667            if (committedText.length() == 1) {
3668        KeyStroke k = KeyStroke.getKeyStroke(committedText.charAt(0));
3669                a = binding.getAction(k);
3670        }
3671
3672        if (a == null) {
3673            a = binding.getDefaultAction();
3674        }
3675
3676            if (a != null) {
3677                ActionEvent ae =
3678                    new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
3679                                    committedText,
3680                                    EventQueue.getMostRecentEventTime(),
3681                                    getCurrentEventModifiers());
3682                a.actionPerformed(ae);
3683            }
3684        }
3685    }
3686
3687    //
3688
// Sets the caret position according to the passed input method
3689
// event. Also, sets/resets composed text caret appropriately.
3690
//
3691
private void setInputMethodCaretPosition(InputMethodEvent e) {
3692    int dot;
3693    
3694    if (composedTextExists()) {
3695        dot = composedTextStart.getOffset();
3696        if (!(caret instanceof ComposedTextCaret)) {
3697        if (composedTextCaret == null) {
3698            composedTextCaret = new ComposedTextCaret();
3699        }
3700        originalCaret = caret;
3701        // Sets composed text caret
3702
exchangeCaret(originalCaret, composedTextCaret);
3703        }
3704
3705        TextHitInfo JavaDoc caretPos = e.getCaret();
3706        if (caretPos != null) {
3707        int index = caretPos.getInsertionIndex();
3708        dot += index;
3709        if (index == 0) {
3710            // Scroll the component if needed so that the composed text
3711
// becomes visible.
3712
try {
3713            Rectangle d = modelToView(dot);
3714            Rectangle end = modelToView(composedTextEnd.getOffset());
3715            Rectangle b = getBounds();
3716            d.x += Math.min(end.x - d.x, b.width);
3717            scrollRectToVisible(d);
3718            } catch (BadLocationException JavaDoc ble) {}
3719        }
3720        }
3721        caret.setDot(dot);
3722    } else if (caret instanceof ComposedTextCaret) {
3723        dot = caret.getDot();
3724        // Restores original caret
3725
exchangeCaret(caret, originalCaret);
3726        caret.setDot(dot);
3727    }
3728    }
3729
3730    private void exchangeCaret(Caret JavaDoc oldCaret, Caret JavaDoc newCaret) {
3731    int blinkRate = oldCaret.getBlinkRate();
3732    setCaret(newCaret);
3733    caret.setBlinkRate(blinkRate);
3734    caret.setVisible(hasFocus());
3735    }
3736
3737    /**
3738     * Returns true if KeyEvents should be synthesized from an InputEvent.
3739     */

3740    private boolean shouldSynthensizeKeyEvents() {
3741        if (!checkedInputOverride) {
3742            checkedInputOverride = true;
3743            needToSendKeyTypedEvent =
3744                             !isProcessInputMethodEventOverridden();
3745        }
3746        return needToSendKeyTypedEvent;
3747    }
3748
3749    //
3750
// Checks whether the client code overrides processInputMethodEvent. If it is overridden,
3751
// need not to generate KeyTyped events for committed text. If it's not, behave as an
3752
// passive input method client.
3753
//
3754
private boolean isProcessInputMethodEventOverridden() {
3755        if (overrideMap == null) {
3756            overrideMap = Collections.synchronizedMap(new HashMap JavaDoc());
3757        }
3758        Boolean JavaDoc retValue = (Boolean JavaDoc)overrideMap.get(getClass().getName());
3759
3760        if (retValue != null) {
3761            return retValue.booleanValue();
3762        }
3763    Boolean JavaDoc ret = (Boolean JavaDoc)AccessController.doPrivileged(new
3764                       PrivilegedAction JavaDoc() {
3765            public Object JavaDoc run() {
3766                return isProcessInputMethodEventOverridden(
3767                                JTextComponent.this.getClass());
3768            }
3769        });
3770
3771    return ret.booleanValue();
3772    }
3773
3774    //
3775
// Checks whether a composed text in this text component
3776
//
3777
boolean composedTextExists() {
3778    return (composedTextStart != null);
3779    }
3780
3781    //
3782
// Caret implementation for editing the composed text.
3783
//
3784
class ComposedTextCaret extends DefaultCaret JavaDoc implements Serializable {
3785    Color bg;
3786
3787    //
3788
// Get the background color of the component
3789
//
3790
public void install(JTextComponent JavaDoc c) {
3791        super.install(c);
3792
3793        Document JavaDoc doc = c.getDocument();
3794        if (doc instanceof StyledDocument JavaDoc) {
3795        StyledDocument JavaDoc sDoc = (StyledDocument JavaDoc)doc;
3796        Element JavaDoc elem = sDoc.getCharacterElement(c.composedTextStart.getOffset());
3797            AttributeSet JavaDoc attr = elem.getAttributes();
3798            bg = sDoc.getBackground(attr);
3799        }
3800    
3801        if (bg == null) {
3802            bg = c.getBackground();
3803        }
3804    }
3805        
3806    //
3807
// Draw caret in XOR mode.
3808
//
3809
public void paint(Graphics g) {
3810        if(isVisible()) {
3811            try {
3812            Rectangle r = component.modelToView(getDot());
3813            g.setXORMode(bg);
3814            g.drawLine(r.x, r.y, r.x, r.y + r.height - 1);
3815            g.setPaintMode();
3816            } catch (BadLocationException JavaDoc e) {
3817            // can't render I guess
3818
//System.err.println("Can't render cursor");
3819
}
3820        }
3821    }
3822
3823    //
3824
// If some area other than the composed text is clicked by mouse,
3825
// issue endComposition() to force commit the composed text.
3826
//
3827
protected void positionCaret(MouseEvent me) {
3828        JTextComponent JavaDoc host = component;
3829        Point pt = new Point(me.getX(), me.getY());
3830        int offset = host.viewToModel(pt);
3831        int composedStartIndex = host.composedTextStart.getOffset();
3832        if ((offset < composedStartIndex) ||
3833            (offset > composedTextEnd.getOffset())) {
3834        try {
3835            // Issue endComposition
3836
Position JavaDoc newPos = host.getDocument().createPosition(offset);
3837            host.getInputContext().endComposition();
3838
3839            // Post a caret positioning runnable to assure that the positioning
3840
// occurs *after* committing the composed text.
3841
EventQueue.invokeLater(new DoSetCaretPosition(host, newPos));
3842        } catch (BadLocationException JavaDoc ble) {
3843            System.err.println(ble);
3844        }
3845        } else {
3846            // Normal processing
3847
super.positionCaret(me);
3848        }
3849    }
3850    }
3851
3852    //
3853
// Runnable class for invokeLater() to set caret position later.
3854
//
3855
private class DoSetCaretPosition implements Runnable JavaDoc {
3856        JTextComponent JavaDoc host;
3857    Position JavaDoc newPos;
3858
3859    DoSetCaretPosition(JTextComponent JavaDoc host, Position JavaDoc newPos) {
3860        this.host = host;
3861        this.newPos = newPos;
3862    }
3863
3864    public void run() {
3865        host.setCaretPosition(newPos.getOffset());
3866    }
3867    }
3868}
3869
3870
3871
Popular Tags