1. /*
  2. * @(#)JTextComponent.java 1.212 04/04/15
  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. import java.lang.reflect.Method;
  9. import java.security.AccessController;
  10. import java.security.PrivilegedAction;
  11. import java.util.Collections;
  12. import java.util.HashMap;
  13. import java.util.Hashtable;
  14. import java.util.Enumeration;
  15. import java.util.Vector;
  16. import java.util.Iterator;
  17. import java.util.Map;
  18. import java.util.Map.Entry;
  19. import java.util.Set;
  20. import java.io.*;
  21. import java.awt.*;
  22. import java.awt.event.*;
  23. import java.awt.datatransfer.*;
  24. import java.awt.im.InputContext;
  25. import java.awt.im.InputMethodRequests;
  26. import java.awt.font.TextHitInfo;
  27. import java.awt.font.TextAttribute;
  28. import java.text.*;
  29. import java.text.AttributedCharacterIterator.Attribute;
  30. import javax.swing.*;
  31. import javax.swing.event.*;
  32. import javax.swing.plaf.*;
  33. import javax.accessibility.*;
  34. import sun.awt.AppContext;
  35. /**
  36. * <code>JTextComponent</code> is the base class for swing text
  37. * components. It tries to be compatible with the
  38. * <code>java.awt.TextComponent</code> class
  39. * where it can reasonably do so. Also provided are other services
  40. * for additional flexibility (beyond the pluggable UI and bean
  41. * support).
  42. * You can find information on how to use the functionality
  43. * this class provides in
  44. * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/generaltext.html">General Rules for Using Text Components</a>,
  45. * a section in <em>The Java Tutorial.</em>
  46. *
  47. * <p>
  48. * <dl>
  49. * <dt><b><font size=+1>Caret Changes</font></b>
  50. * <dd>
  51. * The caret is a pluggable object in swing text components.
  52. * Notification of changes to the caret position and the selection
  53. * are sent to implementations of the <code>CaretListener</code>
  54. * interface that have been registered with the text component.
  55. * The UI will install a default caret unless a customized caret
  56. * has been set. <br>
  57. * By default the caret tracks all the document changes
  58. * performed on the Event Dispatching Thread and updates it's position
  59. * accordingly if an insertion occurs before or at the caret position
  60. * or a removal occurs before the caret position. <code>DefaultCaret</code>
  61. * tries to make itself visible which may lead to scrolling
  62. * of a text component within <code>JScrollPane</code>. The default caret
  63. * behavior can be changed by the {@link DefaultCaret#setUpdatePolicy} method.
  64. * <br>
  65. * <b>Note</b>: Non-editable text components also have a caret though
  66. * it may not be painted.
  67. *
  68. * <p>
  69. * <dt><b><font size=+1>Commands</font></b>
  70. * <dd>
  71. * Text components provide a number of commands that can be used
  72. * to manipulate the component. This is essentially the way that
  73. * the component expresses its capabilities. These are expressed
  74. * in terms of the swing <code>Action</code> interface,
  75. * using the <code>TextAction</code> implementation.
  76. * The set of commands supported by the text component can be
  77. * found with the {@link #getActions} method. These actions
  78. * can be bound to key events, fired from buttons, etc.
  79. *
  80. * <p>
  81. * <dt><b><font size=+1>Text Input</font></b>
  82. * <dd>
  83. * The text components support flexible and internationalized text input, using
  84. * keymaps and the input method framework, while maintaining compatibility with
  85. * the AWT listener model.
  86. * <p>
  87. * A {@link javax.swing.text.Keymap} lets an application bind key
  88. * strokes to actions.
  89. * In order to allow keymaps to be shared across multiple text components, they
  90. * can use actions that extend <code>TextAction</code>.
  91. * <code>TextAction</code> can determine which <code>JTextComponent</code>
  92. * most recently has or had focus and therefore is the subject of
  93. * the action (In the case that the <code>ActionEvent</code>
  94. * sent to the action doesn't contain the target text component as its source).
  95. * <p>
  96. * The <a href="../../../../guide/imf/spec.html">input method framework</a>
  97. * lets text components interact with input methods, separate software
  98. * components that preprocess events to let users enter thousands of
  99. * different characters using keyboards with far fewer keys.
  100. * <code>JTextComponent</code> is an <em>active client</em> of
  101. * the framework, so it implements the preferred user interface for interacting
  102. * with input methods. As a consequence, some key events do not reach the text
  103. * component because they are handled by an input method, and some text input
  104. * reaches the text component as committed text within an {@link
  105. * java.awt.event.InputMethodEvent} instead of as a key event.
  106. * The complete text input is the combination of the characters in
  107. * <code>keyTyped</code> key events and committed text in input method events.
  108. * <p>
  109. * The AWT listener model lets applications attach event listeners to
  110. * components in order to bind events to actions. Swing encourages the
  111. * use of keymaps instead of listeners, but maintains compatibility
  112. * with listeners by giving the listeners a chance to steal an event
  113. * by consuming it.
  114. * <p>
  115. * Keyboard event and input method events are handled in the following stages,
  116. * with each stage capable of consuming the event:
  117. *
  118. * <table border=1 summary="Stages of keyboard and input method event handling">
  119. * <tr>
  120. * <th id="stage"><p align="left">Stage</p></th>
  121. * <th id="ke"><p align="left">KeyEvent</p></th>
  122. * <th id="ime"><p align="left">InputMethodEvent</p></th></tr>
  123. * <tr><td headers="stage">1. </td>
  124. * <td headers="ke">input methods </td>
  125. * <td headers="ime">(generated here)</td></tr>
  126. * <tr><td headers="stage">2. </td>
  127. * <td headers="ke">focus manager </td>
  128. * <td headers="ime"></td>
  129. * </tr>
  130. * <tr>
  131. * <td headers="stage">3. </td>
  132. * <td headers="ke">registered key listeners</td>
  133. * <td headers="ime">registered input method listeners</tr>
  134. * <tr>
  135. * <td headers="stage">4. </td>
  136. * <td headers="ke"></td>
  137. * <td headers="ime">input method handling in JTextComponent</tr>
  138. * <tr>
  139. * <td headers="stage">5. </td><td headers="ke ime" colspan=2>keymap handling using the current keymap</td></tr>
  140. * <tr><td headers="stage">6. </td><td headers="ke">keyboard handling in JComponent (e.g. accelerators, component navigation, etc.)</td>
  141. * <td headers="ime"></td></tr>
  142. * </table>
  143. *
  144. * <p>
  145. * To maintain compatibility with applications that listen to key
  146. * events but are not aware of input method events, the input
  147. * method handling in stage 4 provides a compatibility mode for
  148. * components that do not process input method events. For these
  149. * components, the committed text is converted to keyTyped key events
  150. * and processed in the key event pipeline starting at stage 3
  151. * instead of in the input method event pipeline.
  152. * <p>
  153. * By default the component will create a keymap (named <b>DEFAULT_KEYMAP</b>)
  154. * that is shared by all JTextComponent instances as the default keymap.
  155. * Typically a look-and-feel implementation will install a different keymap
  156. * that resolves to the default keymap for those bindings not found in the
  157. * different keymap. The minimal bindings include:
  158. * <ul>
  159. * <li>inserting content into the editor for the
  160. * printable keys.
  161. * <li>removing content with the backspace and del
  162. * keys.
  163. * <li>caret movement forward and backward
  164. * </ul>
  165. *
  166. * <p>
  167. * <dt><b><font size=+1>Model/View Split</font></b>
  168. * <dd>
  169. * The text components have a model-view split. A text component pulls
  170. * together the objects used to represent the model, view, and controller.
  171. * The text document model may be shared by other views which act as observers
  172. * of the model (e.g. a document may be shared by multiple components).
  173. *
  174. * <p align=center><img src="doc-files/editor.gif" alt="Diagram showing interaction between Controller, Document, events, and ViewFactory"
  175. * HEIGHT=358 WIDTH=587></p>
  176. *
  177. * <p>
  178. * The model is defined by the {@link Document} interface.
  179. * This is intended to provide a flexible text storage mechanism
  180. * that tracks change during edits and can be extended to more sophisticated
  181. * models. The model interfaces are meant to capture the capabilities of
  182. * expression given by SGML, a system used to express a wide variety of
  183. * content.
  184. * Each modification to the document causes notification of the
  185. * details of the change to be sent to all observers in the form of a
  186. * {@link DocumentEvent} which allows the views to stay up to date with the model.
  187. * This event is sent to observers that have implemented the
  188. * {@link DocumentListener}
  189. * interface and registered interest with the model being observed.
  190. *
  191. * <p>
  192. * <dt><b><font size=+1>Location Information</font></b>
  193. * <dd>
  194. * The capability of determining the location of text in
  195. * the view is provided. There are two methods, {@link #modelToView}
  196. * and {@link #viewToModel} for determining this information.
  197. *
  198. * <p>
  199. * <dt><b><font size=+1>Undo/Redo support</font></b>
  200. * <dd>
  201. * Support for an edit history mechanism is provided to allow
  202. * undo/redo operations. The text component does not itself
  203. * provide the history buffer by default, but does provide
  204. * the <code>UndoableEdit</code> records that can be used in conjunction
  205. * with a history buffer to provide the undo/redo support.
  206. * The support is provided by the Document model, which allows
  207. * one to attach UndoableEditListener implementations.
  208. *
  209. * <p>
  210. * <dt><b><font size=+1>Thread Safety</font></b>
  211. * <dd>
  212. * The swing text components provide some support of thread
  213. * safe operations. Because of the high level of configurability
  214. * of the text components, it is possible to circumvent the
  215. * protection provided. The protection primarily comes from
  216. * the model, so the documentation of <code>AbstractDocument</code>
  217. * describes the assumptions of the protection provided.
  218. * The methods that are safe to call asynchronously are marked
  219. * with comments.
  220. *
  221. * <p>
  222. * <dt><b><font size=+1>Newlines</font></b>
  223. * <dd>
  224. * For a discussion on how newlines are handled, see
  225. * <a href="DefaultEditorKit.html">DefaultEditorKit</a>.
  226. * </dl>
  227. *
  228. * <p>
  229. * <strong>Warning:</strong>
  230. * Serialized objects of this class will not be compatible with
  231. * future Swing releases. The current serialization support is
  232. * appropriate for short term storage or RMI between applications running
  233. * the same version of Swing. As of 1.4, support for long term storage
  234. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  235. * has been added to the <code>java.beans</code> package.
  236. * Please see {@link java.beans.XMLEncoder}.
  237. *
  238. * @beaninfo
  239. * attribute: isContainer false
  240. *
  241. * @author Timothy Prinzing
  242. * @version 1.212 04/15/04
  243. * @see Document
  244. * @see DocumentEvent
  245. * @see DocumentListener
  246. * @see Caret
  247. * @see CaretEvent
  248. * @see CaretListener
  249. * @see TextUI
  250. * @see View
  251. * @see ViewFactory
  252. */
  253. public abstract class JTextComponent extends JComponent implements Scrollable, Accessible
  254. {
  255. /**
  256. * Creates a new <code>JTextComponent</code>.
  257. * Listeners for caret events are established, and the pluggable
  258. * UI installed. The component is marked as editable. No layout manager
  259. * is used, because layout is managed by the view subsystem of text.
  260. * The document model is set to <code>null</code>.
  261. */
  262. public JTextComponent() {
  263. super();
  264. // enable InputMethodEvent for on-the-spot pre-editing
  265. enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.INPUT_METHOD_EVENT_MASK);
  266. caretEvent = new MutableCaretEvent(this);
  267. addMouseListener(caretEvent);
  268. addFocusListener(caretEvent);
  269. setEditable(true);
  270. setDragEnabled(false);
  271. setLayout(null); // layout is managed by View hierarchy
  272. updateUI();
  273. }
  274. /**
  275. * Fetches the user-interface factory for this text-oriented editor.
  276. *
  277. * @return the factory
  278. */
  279. public TextUI getUI() { return (TextUI)ui; }
  280. /**
  281. * Sets the user-interface factory for this text-oriented editor.
  282. *
  283. * @param ui the factory
  284. */
  285. public void setUI(TextUI ui) {
  286. super.setUI(ui);
  287. }
  288. /**
  289. * Reloads the pluggable UI. The key used to fetch the
  290. * new interface is <code>getUIClassID()</code>. The type of
  291. * the UI is <code>TextUI</code>. <code>invalidate</code>
  292. * is called after setting the UI.
  293. */
  294. public void updateUI() {
  295. setUI((TextUI)UIManager.getUI(this));
  296. invalidate();
  297. }
  298. /**
  299. * Adds a caret listener for notification of any changes
  300. * to the caret.
  301. *
  302. * @param listener the listener to be added
  303. * @see javax.swing.event.CaretEvent
  304. */
  305. public void addCaretListener(CaretListener listener) {
  306. listenerList.add(CaretListener.class, listener);
  307. }
  308. /**
  309. * Removes a caret listener.
  310. *
  311. * @param listener the listener to be removed
  312. * @see javax.swing.event.CaretEvent
  313. */
  314. public void removeCaretListener(CaretListener listener) {
  315. listenerList.remove(CaretListener.class, listener);
  316. }
  317. /**
  318. * Returns an array of all the caret listeners
  319. * registered on this text component.
  320. *
  321. * @return all of this component's <code>CaretListener</code>s
  322. * or an empty
  323. * array if no caret listeners are currently registered
  324. *
  325. * @see #addCaretListener
  326. * @see #removeCaretListener
  327. *
  328. * @since 1.4
  329. */
  330. public CaretListener[] getCaretListeners() {
  331. return (CaretListener[])listenerList.getListeners(CaretListener.class);
  332. }
  333. /**
  334. * Notifies all listeners that have registered interest for
  335. * notification on this event type. The event instance
  336. * is lazily created using the parameters passed into
  337. * the fire method. The listener list is processed in a
  338. * last-to-first manner.
  339. *
  340. * @param e the event
  341. * @see EventListenerList
  342. */
  343. protected void fireCaretUpdate(CaretEvent e) {
  344. // Guaranteed to return a non-null array
  345. Object[] listeners = listenerList.getListenerList();
  346. // Process the listeners last to first, notifying
  347. // those that are interested in this event
  348. for (int i = listeners.length-2; i>=0; i-=2) {
  349. if (listeners[i]==CaretListener.class) {
  350. ((CaretListener)listeners[i+1]).caretUpdate(e);
  351. }
  352. }
  353. }
  354. /**
  355. * Associates the editor with a text document.
  356. * The currently registered factory is used to build a view for
  357. * the document, which gets displayed by the editor after revalidation.
  358. * A PropertyChange event ("document") is propagated to each listener.
  359. *
  360. * @param doc the document to display/edit
  361. * @see #getDocument
  362. * @beaninfo
  363. * description: the text document model
  364. * bound: true
  365. * expert: true
  366. */
  367. public void setDocument(Document doc) {
  368. Document old = model;
  369. /*
  370. * aquire a read lock on the old model to prevent notification of
  371. * mutations while we disconnecting the old model.
  372. */
  373. try {
  374. if (old instanceof AbstractDocument) {
  375. ((AbstractDocument)old).readLock();
  376. }
  377. if (accessibleContext != null) {
  378. model.removeDocumentListener(
  379. ((AccessibleJTextComponent)accessibleContext));
  380. }
  381. if (inputMethodRequestsHandler != null) {
  382. model.removeDocumentListener((DocumentListener)inputMethodRequestsHandler);
  383. }
  384. model = doc;
  385. // Set the document's run direction property to match the
  386. // component's ComponentOrientation property.
  387. Boolean runDir = getComponentOrientation().isLeftToRight()
  388. ? TextAttribute.RUN_DIRECTION_LTR
  389. : TextAttribute.RUN_DIRECTION_RTL;
  390. doc.putProperty( TextAttribute.RUN_DIRECTION, runDir );
  391. firePropertyChange("document", old, doc);
  392. } finally {
  393. if (old instanceof AbstractDocument) {
  394. ((AbstractDocument)old).readUnlock();
  395. }
  396. }
  397. revalidate();
  398. repaint();
  399. if (accessibleContext != null) {
  400. model.addDocumentListener(
  401. ((AccessibleJTextComponent)accessibleContext));
  402. }
  403. if (inputMethodRequestsHandler != null) {
  404. model.addDocumentListener((DocumentListener)inputMethodRequestsHandler);
  405. }
  406. }
  407. /**
  408. * Fetches the model associated with the editor. This is
  409. * primarily for the UI to get at the minimal amount of
  410. * state required to be a text editor. Subclasses will
  411. * return the actual type of the model which will typically
  412. * be something that extends Document.
  413. *
  414. * @return the model
  415. */
  416. public Document getDocument() {
  417. return model;
  418. }
  419. // Override of Component.setComponentOrientation
  420. public void setComponentOrientation( ComponentOrientation o ) {
  421. // Set the document's run direction property to match the
  422. // ComponentOrientation property.
  423. Document doc = getDocument();
  424. if( doc != null ) {
  425. Boolean runDir = o.isLeftToRight()
  426. ? TextAttribute.RUN_DIRECTION_LTR
  427. : TextAttribute.RUN_DIRECTION_RTL;
  428. doc.putProperty( TextAttribute.RUN_DIRECTION, runDir );
  429. }
  430. super.setComponentOrientation( o );
  431. }
  432. /**
  433. * Fetches the command list for the editor. This is
  434. * the list of commands supported by the plugged-in UI
  435. * augmented by the collection of commands that the
  436. * editor itself supports. These are useful for binding
  437. * to events, such as in a keymap.
  438. *
  439. * @return the command list
  440. */
  441. public Action[] getActions() {
  442. return getUI().getEditorKit(this).getActions();
  443. }
  444. /**
  445. * Sets margin space between the text component's border
  446. * and its text. The text component's default <code>Border</code>
  447. * object will use this value to create the proper margin.
  448. * However, if a non-default border is set on the text component,
  449. * it is that <code>Border</code> object's responsibility to create the
  450. * appropriate margin space (else this property will effectively
  451. * be ignored). This causes a redraw of the component.
  452. * A PropertyChange event ("margin") is sent to all listeners.
  453. *
  454. * @param m the space between the border and the text
  455. * @beaninfo
  456. * description: desired space between the border and text area
  457. * bound: true
  458. */
  459. public void setMargin(Insets m) {
  460. Insets old = margin;
  461. margin = m;
  462. firePropertyChange("margin", old, m);
  463. invalidate();
  464. }
  465. /**
  466. * Returns the margin between the text component's border and
  467. * its text.
  468. *
  469. * @return the margin
  470. */
  471. public Insets getMargin() {
  472. return margin;
  473. }
  474. /**
  475. * Sets the <code>NavigationFilter</code>. <code>NavigationFilter</code>
  476. * is used by <code>DefaultCaret</code> and the default cursor movement
  477. * actions as a way to restrict the cursor movement.
  478. *
  479. * @since 1.4
  480. */
  481. public void setNavigationFilter(NavigationFilter filter) {
  482. navigationFilter = filter;
  483. }
  484. /**
  485. * Returns the <code>NavigationFilter</code>. <code>NavigationFilter</code>
  486. * is used by <code>DefaultCaret</code> and the default cursor movement
  487. * actions as a way to restrict the cursor movement. A null return value
  488. * implies the cursor movement and selection should not be restricted.
  489. *
  490. * @since 1.4
  491. * @return the NavigationFilter
  492. */
  493. public NavigationFilter getNavigationFilter() {
  494. return navigationFilter;
  495. }
  496. /**
  497. * Fetches the caret that allows text-oriented navigation over
  498. * the view.
  499. *
  500. * @return the caret
  501. */
  502. public Caret getCaret() {
  503. return caret;
  504. }
  505. /**
  506. * Sets the caret to be used. By default this will be set
  507. * by the UI that gets installed. This can be changed to
  508. * a custom caret if desired. Setting the caret results in a
  509. * PropertyChange event ("caret") being fired.
  510. *
  511. * @param c the caret
  512. * @see #getCaret
  513. * @beaninfo
  514. * description: the caret used to select/navigate
  515. * bound: true
  516. * expert: true
  517. */
  518. public void setCaret(Caret c) {
  519. if (caret != null) {
  520. caret.removeChangeListener(caretEvent);
  521. caret.deinstall(this);
  522. }
  523. Caret old = caret;
  524. caret = c;
  525. if (caret != null) {
  526. caret.install(this);
  527. caret.addChangeListener(caretEvent);
  528. }
  529. firePropertyChange("caret", old, caret);
  530. }
  531. /**
  532. * Fetches the object responsible for making highlights.
  533. *
  534. * @return the highlighter
  535. */
  536. public Highlighter getHighlighter() {
  537. return highlighter;
  538. }
  539. /**
  540. * Sets the highlighter to be used. By default this will be set
  541. * by the UI that gets installed. This can be changed to
  542. * a custom highlighter if desired. The highlighter can be set to
  543. * <code>null</code> to disable it.
  544. * A PropertyChange event ("highlighter") is fired
  545. * when a new highlighter is installed.
  546. *
  547. * @param h the highlighter
  548. * @see #getHighlighter
  549. * @beaninfo
  550. * description: object responsible for background highlights
  551. * bound: true
  552. * expert: true
  553. */
  554. public void setHighlighter(Highlighter h) {
  555. if (highlighter != null) {
  556. highlighter.deinstall(this);
  557. }
  558. Highlighter old = highlighter;
  559. highlighter = h;
  560. if (highlighter != null) {
  561. highlighter.install(this);
  562. }
  563. firePropertyChange("highlighter", old, h);
  564. }
  565. /**
  566. * Sets the keymap to use for binding events to
  567. * actions. Setting to <code>null</code> effectively disables
  568. * keyboard input.
  569. * A PropertyChange event ("keymap") is fired when a new keymap
  570. * is installed.
  571. *
  572. * @param map the keymap
  573. * @see #getKeymap
  574. * @beaninfo
  575. * description: set of key event to action bindings to use
  576. * bound: true
  577. */
  578. public void setKeymap(Keymap map) {
  579. Keymap old = keymap;
  580. keymap = map;
  581. firePropertyChange("keymap", old, keymap);
  582. updateInputMap(old, map);
  583. }
  584. /**
  585. * Sets the <code>dragEnabled</code> property,
  586. * which must be <code>true</code> to enable
  587. * automatic drag handling (the first part of drag and drop)
  588. * on this component.
  589. * The <code>transferHandler</code> property needs to be set
  590. * to a non-<code>null</code> value for the drag to do
  591. * anything. The default value of the <code>dragEnabled</code>
  592. * property
  593. * is <code>false</code>.
  594. * <p>
  595. * When automatic drag handling is enabled,
  596. * most look and feels begin a drag-and-drop operation
  597. * whenever the user presses the mouse button over a selection
  598. * and then moves the mouse a few pixels.
  599. * Setting this property to <code>true</code>
  600. * can therefore have a subtle effect on
  601. * how selections behave.
  602. * <p>
  603. * Some look and feels might not support automatic drag and drop;
  604. * they will ignore this property. You can work around such
  605. * look and feels by modifying the component
  606. * to directly call the <code>exportAsDrag</code> method of a
  607. * <code>TransferHandler</code>.
  608. *
  609. * @param b the value to set the <code>dragEnabled</code> property to
  610. * @exception HeadlessException if
  611. * <code>b</code> is <code>true</code> and
  612. * <code>GraphicsEnvironment.isHeadless()</code>
  613. * returns <code>true</code>
  614. * @see java.awt.GraphicsEnvironment#isHeadless
  615. * @see #getDragEnabled
  616. * @see #setTransferHandler
  617. * @see TransferHandler
  618. * @since 1.4
  619. *
  620. * @beaninfo
  621. * description: determines whether automatic drag handling is enabled
  622. * bound: false
  623. */
  624. public void setDragEnabled(boolean b) {
  625. if (b && GraphicsEnvironment.isHeadless()) {
  626. throw new HeadlessException();
  627. }
  628. dragEnabled = b;
  629. }
  630. /**
  631. * Gets the <code>dragEnabled</code> property.
  632. *
  633. * @return the value of the <code>dragEnabled</code> property
  634. * @see #setDragEnabled
  635. * @since 1.4
  636. */
  637. public boolean getDragEnabled() {
  638. return dragEnabled;
  639. }
  640. /**
  641. * Updates the <code>InputMap</code>s in response to a
  642. * <code>Keymap</code> change.
  643. * @param oldKm the old <code>Keymap</code>
  644. * @param newKm the new <code>Keymap</code>
  645. */
  646. void updateInputMap(Keymap oldKm, Keymap newKm) {
  647. // Locate the current KeymapWrapper.
  648. InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
  649. InputMap last = km;
  650. while (km != null && !(km instanceof KeymapWrapper)) {
  651. last = km;
  652. km = km.getParent();
  653. }
  654. if (km != null) {
  655. // Found it, tweak the InputMap that points to it, as well
  656. // as anything it points to.
  657. if (newKm == null) {
  658. if (last != km) {
  659. last.setParent(km.getParent());
  660. }
  661. else {
  662. last.setParent(null);
  663. }
  664. }
  665. else {
  666. InputMap newKM = new KeymapWrapper(newKm);
  667. last.setParent(newKM);
  668. if (last != km) {
  669. newKM.setParent(km.getParent());
  670. }
  671. }
  672. }
  673. else if (newKm != null) {
  674. km = getInputMap(JComponent.WHEN_FOCUSED);
  675. if (km != null) {
  676. // Couldn't find it.
  677. // Set the parent of WHEN_FOCUSED InputMap to be the new one.
  678. InputMap newKM = new KeymapWrapper(newKm);
  679. newKM.setParent(km.getParent());
  680. km.setParent(newKM);
  681. }
  682. }
  683. // Do the same thing with the ActionMap
  684. ActionMap am = getActionMap();
  685. ActionMap lastAM = am;
  686. while (am != null && !(am instanceof KeymapActionMap)) {
  687. lastAM = am;
  688. am = am.getParent();
  689. }
  690. if (am != null) {
  691. // Found it, tweak the Actionap that points to it, as well
  692. // as anything it points to.
  693. if (newKm == null) {
  694. if (lastAM != am) {
  695. lastAM.setParent(am.getParent());
  696. }
  697. else {
  698. lastAM.setParent(null);
  699. }
  700. }
  701. else {
  702. ActionMap newAM = new KeymapActionMap(newKm);
  703. lastAM.setParent(newAM);
  704. if (lastAM != am) {
  705. newAM.setParent(am.getParent());
  706. }
  707. }
  708. }
  709. else if (newKm != null) {
  710. am = getActionMap();
  711. if (am != null) {
  712. // Couldn't find it.
  713. // Set the parent of ActionMap to be the new one.
  714. ActionMap newAM = new KeymapActionMap(newKm);
  715. newAM.setParent(am.getParent());
  716. am.setParent(newAM);
  717. }
  718. }
  719. }
  720. /**
  721. * Fetches the keymap currently active in this text
  722. * component.
  723. *
  724. * @return the keymap
  725. */
  726. public Keymap getKeymap() {
  727. return keymap;
  728. }
  729. /**
  730. * Adds a new keymap into the keymap hierarchy. Keymap bindings
  731. * resolve from bottom up so an attribute specified in a child
  732. * will override an attribute specified in the parent.
  733. *
  734. * @param nm the name of the keymap (must be unique within the
  735. * collection of named keymaps in the document); the name may
  736. * be <code>null</code> if the keymap is unnamed,
  737. * but the caller is responsible for managing the reference
  738. * returned as an unnamed keymap can't
  739. * be fetched by name
  740. * @param parent the parent keymap; this may be <code>null</code> if
  741. * unspecified bindings need not be resolved in some other keymap
  742. * @return the keymap
  743. */
  744. public static Keymap addKeymap(String nm, Keymap parent) {
  745. Keymap map = new DefaultKeymap(nm, parent);
  746. if (nm != null) {
  747. // add a named keymap, a class of bindings
  748. getKeymapTable().put(nm, map);
  749. }
  750. return map;
  751. }
  752. /**
  753. * Removes a named keymap previously added to the document. Keymaps
  754. * with <code>null</code> names may not be removed in this way.
  755. *
  756. * @param nm the name of the keymap to remove
  757. * @return the keymap that was removed
  758. */
  759. public static Keymap removeKeymap(String nm) {
  760. return getKeymapTable().remove(nm);
  761. }
  762. /**
  763. * Fetches a named keymap previously added to the document.
  764. * This does not work with <code>null</code>-named keymaps.
  765. *
  766. * @param nm the name of the keymap
  767. * @return the keymap
  768. */
  769. public static Keymap getKeymap(String nm) {
  770. return getKeymapTable().get(nm);
  771. }
  772. private static HashMap<String,Keymap> getKeymapTable() {
  773. AppContext appContext = AppContext.getAppContext();
  774. HashMap<String,Keymap> keymapTable =
  775. (HashMap<String,Keymap>)appContext.get(KEYMAP_TABLE);
  776. if (keymapTable == null) {
  777. keymapTable = new HashMap<String,Keymap>(17);
  778. appContext.put(KEYMAP_TABLE, keymapTable);
  779. //initialize default keymap
  780. Keymap binding = addKeymap(DEFAULT_KEYMAP, null);
  781. binding.setDefaultAction(new
  782. DefaultEditorKit.DefaultKeyTypedAction());
  783. }
  784. return keymapTable;
  785. }
  786. /**
  787. * Binding record for creating key bindings.
  788. * <p>
  789. * <strong>Warning:</strong>
  790. * Serialized objects of this class will not be compatible with
  791. * future Swing releases. The current serialization support is
  792. * appropriate for short term storage or RMI between applications running
  793. * the same version of Swing. As of 1.4, support for long term storage
  794. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  795. * has been added to the <code>java.beans</code> package.
  796. * Please see {@link java.beans.XMLEncoder}.
  797. */
  798. public static class KeyBinding {
  799. /**
  800. * The key.
  801. */
  802. public KeyStroke key;
  803. /**
  804. * The name of the action for the key.
  805. */
  806. public String actionName;
  807. /**
  808. * Creates a new key binding.
  809. *
  810. * @param key the key
  811. * @param actionName the name of the action for the key
  812. */
  813. public KeyBinding(KeyStroke key, String actionName) {
  814. this.key = key;
  815. this.actionName = actionName;
  816. }
  817. }
  818. /**
  819. * <p>
  820. * Loads a keymap with a bunch of
  821. * bindings. This can be used to take a static table of
  822. * definitions and load them into some keymap. The following
  823. * example illustrates an example of binding some keys to
  824. * the cut, copy, and paste actions associated with a
  825. * JTextComponent. A code fragment to accomplish
  826. * this might look as follows:
  827. * <pre><code>
  828. *
  829. * static final JTextComponent.KeyBinding[] defaultBindings = {
  830. * new JTextComponent.KeyBinding(
  831. * KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
  832. * DefaultEditorKit.copyAction),
  833. * new JTextComponent.KeyBinding(
  834. * KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
  835. * DefaultEditorKit.pasteAction),
  836. * new JTextComponent.KeyBinding(
  837. * KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
  838. * DefaultEditorKit.cutAction),
  839. * };
  840. *
  841. * JTextComponent c = new JTextPane();
  842. * Keymap k = c.getKeymap();
  843. * JTextComponent.loadKeymap(k, defaultBindings, c.getActions());
  844. *
  845. * </code></pre>
  846. * The sets of bindings and actions may be empty but must be
  847. * non-<code>null</code>.
  848. *
  849. * @param map the keymap
  850. * @param bindings the bindings
  851. * @param actions the set of actions
  852. */
  853. public static void loadKeymap(Keymap map, KeyBinding[] bindings, Action[] actions) {
  854. Hashtable h = new Hashtable();
  855. for (int i = 0; i < actions.length; i++) {
  856. Action a = actions[i];
  857. String value = (String)a.getValue(Action.NAME);
  858. h.put((value!=null ? value:""), a);
  859. }
  860. for (int i = 0; i < bindings.length; i++) {
  861. Action a = (Action) h.get(bindings[i].actionName);
  862. if (a != null) {
  863. map.addActionForKeyStroke(bindings[i].key, a);
  864. }
  865. }
  866. }
  867. /**
  868. * Returns true if <code>klass</code> is NOT a JTextComponent and it or
  869. * one of its superclasses (stoping at JTextComponent) overrides
  870. * <code>processInputMethodEvent</code>. It is assumed this will be
  871. * invoked from within a <code>doPrivileged</code>, and it is also
  872. * assumed <code>klass</code> extends <code>JTextComponent</code>.
  873. */
  874. private static Boolean isProcessInputMethodEventOverridden(Class klass) {
  875. if (klass == JTextComponent.class) {
  876. return Boolean.FALSE;
  877. }
  878. Boolean retValue = (Boolean)overrideMap.get(klass.getName());
  879. if (retValue != null) {
  880. return retValue;
  881. }
  882. Boolean sOverriden = isProcessInputMethodEventOverridden(
  883. klass.getSuperclass());
  884. if (sOverriden.booleanValue()) {
  885. // If our superclass has overriden it, then by definition klass
  886. // overrides it.
  887. overrideMap.put(klass.getName(), sOverriden);
  888. return sOverriden;
  889. }
  890. // klass's superclass didn't override it, check for an override in
  891. // klass.
  892. try {
  893. Class[] classes = new Class[1];
  894. classes[0] = InputMethodEvent.class;
  895. Method m = klass.getDeclaredMethod("processInputMethodEvent",
  896. classes);
  897. retValue = Boolean.TRUE;
  898. } catch (NoSuchMethodException nsme) {
  899. retValue = Boolean.FALSE;
  900. }
  901. overrideMap.put(klass.getName(), retValue);
  902. return retValue;
  903. }
  904. /**
  905. * Fetches the current color used to render the
  906. * caret.
  907. *
  908. * @return the color
  909. */
  910. public Color getCaretColor() {
  911. return caretColor;
  912. }
  913. /**
  914. * Sets the current color used to render the caret.
  915. * Setting to <code>null</code> effectively restores the default color.
  916. * Setting the color results in a PropertyChange event ("caretColor")
  917. * being fired.
  918. *
  919. * @param c the color
  920. * @see #getCaretColor
  921. * @beaninfo
  922. * description: the color used to render the caret
  923. * bound: true
  924. * preferred: true
  925. */
  926. public void setCaretColor(Color c) {
  927. Color old = caretColor;
  928. caretColor = c;
  929. firePropertyChange("caretColor", old, caretColor);
  930. }
  931. /**
  932. * Fetches the current color used to render the
  933. * selection.
  934. *
  935. * @return the color
  936. */
  937. public Color getSelectionColor() {
  938. return selectionColor;
  939. }
  940. /**
  941. * Sets the current color used to render the selection.
  942. * Setting the color to <code>null</code> is the same as setting
  943. * <code>Color.white</code>. Setting the color results in a
  944. * PropertyChange event ("selectionColor").
  945. *
  946. * @param c the color
  947. * @see #getSelectionColor
  948. * @beaninfo
  949. * description: color used to render selection background
  950. * bound: true
  951. * preferred: true
  952. */
  953. public void setSelectionColor(Color c) {
  954. Color old = selectionColor;
  955. selectionColor = c;
  956. firePropertyChange("selectionColor", old, selectionColor);
  957. }
  958. /**
  959. * Fetches the current color used to render the
  960. * selected text.
  961. *
  962. * @return the color
  963. */
  964. public Color getSelectedTextColor() {
  965. return selectedTextColor;
  966. }
  967. /**
  968. * Sets the current color used to render the selected text.
  969. * Setting the color to <code>null</code> is the same as
  970. * <code>Color.black</code>. Setting the color results in a
  971. * PropertyChange event ("selectedTextColor") being fired.
  972. *
  973. * @param c the color
  974. * @see #getSelectedTextColor
  975. * @beaninfo
  976. * description: color used to render selected text
  977. * bound: true
  978. * preferred: true
  979. */
  980. public void setSelectedTextColor(Color c) {
  981. Color old = selectedTextColor;
  982. selectedTextColor = c;
  983. firePropertyChange("selectedTextColor", old, selectedTextColor);
  984. }
  985. /**
  986. * Fetches the current color used to render the
  987. * selected text.
  988. *
  989. * @return the color
  990. */
  991. public Color getDisabledTextColor() {
  992. return disabledTextColor;
  993. }
  994. /**
  995. * Sets the current color used to render the
  996. * disabled text. Setting the color fires off a
  997. * PropertyChange event ("disabledTextColor").
  998. *
  999. * @param c the color
  1000. * @see #getDisabledTextColor
  1001. * @beaninfo
  1002. * description: color used to render disabled text
  1003. * bound: true
  1004. * preferred: true
  1005. */
  1006. public void setDisabledTextColor(Color c) {
  1007. Color old = disabledTextColor;
  1008. disabledTextColor = c;
  1009. firePropertyChange("disabledTextColor", old, disabledTextColor);
  1010. }
  1011. /**
  1012. * Replaces the currently selected content with new content
  1013. * represented by the given string. If there is no selection
  1014. * this amounts to an insert of the given text. If there
  1015. * is no replacement text this amounts to a removal of the
  1016. * current selection.
  1017. * <p>
  1018. * This is the method that is used by the default implementation
  1019. * of the action for inserting content that gets bound to the
  1020. * keymap actions.
  1021. * <p>
  1022. * This method is thread safe, although most Swing methods
  1023. * are not. Please see
  1024. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  1025. * and Swing</A> for more information.
  1026. *
  1027. * @param content the content to replace the selection with
  1028. */
  1029. public void replaceSelection(String content) {
  1030. Document doc = getDocument();
  1031. if (doc != null) {
  1032. try {
  1033. boolean composedTextSaved = saveComposedText(caret.getDot());
  1034. int p0 = Math.min(caret.getDot(), caret.getMark());
  1035. int p1 = Math.max(caret.getDot(), caret.getMark());
  1036. if (doc instanceof AbstractDocument) {
  1037. ((AbstractDocument)doc).replace(p0, p1 - p0, content,null);
  1038. }
  1039. else {
  1040. if (p0 != p1) {
  1041. doc.remove(p0, p1 - p0);
  1042. }
  1043. if (content != null && content.length() > 0) {
  1044. doc.insertString(p0, content, null);
  1045. }
  1046. }
  1047. if (composedTextSaved) {
  1048. restoreComposedText();
  1049. }
  1050. } catch (BadLocationException e) {
  1051. UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
  1052. }
  1053. }
  1054. }
  1055. /**
  1056. * Fetches a portion of the text represented by the
  1057. * component. Returns an empty string if length is 0.
  1058. *
  1059. * @param offs the offset >= 0
  1060. * @param len the length >= 0
  1061. * @return the text
  1062. * @exception BadLocationException if the offset or length are invalid
  1063. */
  1064. public String getText(int offs, int len) throws BadLocationException {
  1065. return getDocument().getText(offs, len);
  1066. }
  1067. /**
  1068. * Converts the given location in the model to a place in
  1069. * the view coordinate system.
  1070. * The component must have a positive size for
  1071. * this translation to be computed (i.e. layout cannot
  1072. * be computed until the component has been sized). The
  1073. * component does not have to be visible or painted.
  1074. *
  1075. * @param pos the position >= 0
  1076. * @return the coordinates as a rectangle, with (r.x, r.y) as the location
  1077. * in the coordinate system, or null if the component does
  1078. * not yet have a positive size.
  1079. * @exception BadLocationException if the given position does not
  1080. * represent a valid location in the associated document
  1081. * @see TextUI#modelToView
  1082. */
  1083. public Rectangle modelToView(int pos) throws BadLocationException {
  1084. return getUI().modelToView(this, pos);
  1085. }
  1086. /**
  1087. * Converts the given place in the view coordinate system
  1088. * to the nearest representative location in the model.
  1089. * The component must have a positive size for
  1090. * this translation to be computed (i.e. layout cannot
  1091. * be computed until the component has been sized). The
  1092. * component does not have to be visible or painted.
  1093. *
  1094. * @param pt the location in the view to translate
  1095. * @return the offset >= 0 from the start of the document,
  1096. * or -1 if the component does not yet have a positive
  1097. * size.
  1098. * @see TextUI#viewToModel
  1099. */
  1100. public int viewToModel(Point pt) {
  1101. return getUI().viewToModel(this, pt);
  1102. }
  1103. /**
  1104. * Transfers the currently selected range in the associated
  1105. * text model to the system clipboard, removing the contents
  1106. * from the model. The current selection is reset. Does nothing
  1107. * for <code>null</code> selections.
  1108. *
  1109. * @see java.awt.Toolkit#getSystemClipboard
  1110. * @see java.awt.datatransfer.Clipboard
  1111. */
  1112. public void cut() {
  1113. if (isEditable() && isEnabled()) {
  1114. invokeAction("cut", TransferHandler.getCutAction());
  1115. }
  1116. }
  1117. /**
  1118. * Transfers the currently selected range in the associated
  1119. * text model to the system clipboard, leaving the contents
  1120. * in the text model. The current selection remains intact.
  1121. * Does nothing for <code>null</code> selections.
  1122. *
  1123. * @see java.awt.Toolkit#getSystemClipboard
  1124. * @see java.awt.datatransfer.Clipboard
  1125. */
  1126. public void copy() {
  1127. invokeAction("copy", TransferHandler.getCopyAction());
  1128. }
  1129. /**
  1130. * Transfers the contents of the system clipboard into the
  1131. * associated text model. If there is a selection in the
  1132. * associated view, it is replaced with the contents of the
  1133. * clipboard. If there is no selection, the clipboard contents
  1134. * are inserted in front of the current insert position in
  1135. * the associated view. If the clipboard is empty, does nothing.
  1136. *
  1137. * @see #replaceSelection
  1138. * @see java.awt.Toolkit#getSystemClipboard
  1139. * @see java.awt.datatransfer.Clipboard
  1140. */
  1141. public void paste() {
  1142. if (isEditable() && isEnabled()) {
  1143. invokeAction("paste", TransferHandler.getPasteAction());
  1144. }
  1145. }
  1146. /**
  1147. * This is a conveniance method that is only useful for
  1148. * <code>cut</code>, <code>copy</code> and <code>paste</code>. If
  1149. * an <code>Action</code> with the name <code>name</code> does not
  1150. * exist in the <code>ActionMap</code>, this will attemp to install a
  1151. * <code>TransferHandler</code> and then use <code>altAction</code>.
  1152. */
  1153. private void invokeAction(String name, Action altAction) {
  1154. ActionMap map = getActionMap();
  1155. Action action = null;
  1156. if (map != null) {
  1157. action = map.get(name);
  1158. }
  1159. if (action == null) {
  1160. installDefaultTransferHandlerIfNecessary();
  1161. action = altAction;
  1162. }
  1163. action.actionPerformed(new ActionEvent(this,
  1164. ActionEvent.ACTION_PERFORMED, (String)action.
  1165. getValue(Action.NAME),
  1166. EventQueue.getMostRecentEventTime(),
  1167. getCurrentEventModifiers()));
  1168. }
  1169. /**
  1170. * If the current <code>TransferHandler</code> is null, this will
  1171. * install a new one.
  1172. */
  1173. private void installDefaultTransferHandlerIfNecessary() {
  1174. if (getTransferHandler() == null) {
  1175. if (defaultTransferHandler == null) {
  1176. defaultTransferHandler = new DefaultTransferHandler();
  1177. }
  1178. setTransferHandler(defaultTransferHandler);
  1179. }
  1180. }
  1181. /**
  1182. * Moves the caret to a new position, leaving behind a mark
  1183. * defined by the last time <code>setCaretPosition</code> was
  1184. * called. This forms a selection.
  1185. * If the document is <code>null</code>, does nothing. The position
  1186. * must be between 0 and the length of the component's text or else
  1187. * an exception is thrown.
  1188. *
  1189. * @param pos the position
  1190. * @exception IllegalArgumentException if the value supplied
  1191. * for <code>position</code> is less than zero or greater
  1192. * than the component's text length
  1193. * @see #setCaretPosition
  1194. */
  1195. public void moveCaretPosition(int pos) {
  1196. Document doc = getDocument();
  1197. if (doc != null) {
  1198. if (pos > doc.getLength() || pos < 0) {
  1199. throw new IllegalArgumentException("bad position: " + pos);
  1200. }
  1201. caret.moveDot(pos);
  1202. }
  1203. }
  1204. /**
  1205. * The bound property name for the focus accelerator.
  1206. */
  1207. public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
  1208. /**
  1209. * Sets the key accelerator that will cause the receiving text
  1210. * component to get the focus. The accelerator will be the
  1211. * key combination of the <em>alt</em> key and the character
  1212. * given (converted to upper case). By default, there is no focus
  1213. * accelerator key. Any previous key accelerator setting will be
  1214. * superseded. A '\0' key setting will be registered, and has the
  1215. * effect of turning off the focus accelerator. When the new key
  1216. * is set, a PropertyChange event (FOCUS_ACCELERATOR_KEY) will be fired.
  1217. *
  1218. * @param aKey the key
  1219. * @see #getFocusAccelerator
  1220. * @beaninfo
  1221. * description: accelerator character used to grab focus
  1222. * bound: true
  1223. */
  1224. public void setFocusAccelerator(char aKey) {
  1225. aKey = Character.toUpperCase(aKey);
  1226. char old = focusAccelerator;
  1227. focusAccelerator = aKey;
  1228. // Fix for 4341002: value of FOCUS_ACCELERATOR_KEY is wrong.
  1229. // So we fire both FOCUS_ACCELERATOR_KEY, for compatibility,
  1230. // and the correct event here.
  1231. firePropertyChange(FOCUS_ACCELERATOR_KEY, old, focusAccelerator);
  1232. firePropertyChange("focusAccelerator", old, focusAccelerator);
  1233. }
  1234. /**
  1235. * Returns the key accelerator that will cause the receiving
  1236. * text component to get the focus. Return '\0' if no focus
  1237. * accelerator has been set.
  1238. *
  1239. * @return the key
  1240. */
  1241. public char getFocusAccelerator() {
  1242. return focusAccelerator;
  1243. }
  1244. /**
  1245. * Initializes from a stream. This creates a
  1246. * model of the type appropriate for the component
  1247. * and initializes the model from the stream.
  1248. * By default this will load the model as plain
  1249. * text. Previous contents of the model are discarded.
  1250. *
  1251. * @param in the stream to read from
  1252. * @param desc an object describing the stream; this
  1253. * might be a string, a File, a URL, etc. Some kinds
  1254. * of documents (such as html for example) might be
  1255. * able to make use of this information; if non-<code>null</code>,
  1256. * it is added as a property of the document
  1257. * @exception IOException as thrown by the stream being
  1258. * used to initialize
  1259. * @see EditorKit#createDefaultDocument
  1260. * @see #setDocument
  1261. * @see PlainDocument
  1262. */
  1263. public void read(Reader in, Object desc) throws IOException {
  1264. EditorKit kit = getUI().getEditorKit(this);
  1265. Document doc = kit.createDefaultDocument();
  1266. if (desc != null) {
  1267. doc.putProperty(Document.StreamDescriptionProperty, desc);
  1268. }
  1269. try {
  1270. kit.read(in, doc, 0);
  1271. setDocument(doc);
  1272. } catch (BadLocationException e) {
  1273. throw new IOException(e.getMessage());
  1274. }
  1275. }
  1276. /**
  1277. * Stores the contents of the model into the given
  1278. * stream. By default this will store the model as plain
  1279. * text.
  1280. *
  1281. * @param out the output stream
  1282. * @exception IOException on any I/O error
  1283. */
  1284. public void write(Writer out) throws IOException {
  1285. Document doc = getDocument();
  1286. try {
  1287. getUI().getEditorKit(this).write(out, doc, 0, doc.getLength());
  1288. } catch (BadLocationException e) {
  1289. throw new IOException(e.getMessage());
  1290. }
  1291. }
  1292. public void removeNotify() {
  1293. super.removeNotify();
  1294. if (getFocusedComponent() == this) {
  1295. AppContext.getAppContext().remove(FOCUSED_COMPONENT);
  1296. }
  1297. }
  1298. // --- java.awt.TextComponent methods ------------------------
  1299. /**
  1300. * Sets the position of the text insertion caret for the
  1301. * <code>TextComponent</code>. Note that the caret tracks change,
  1302. * so this may move if the underlying text of the component is changed.
  1303. * If the document is <code>null</code>, does nothing. The position
  1304. * must be between 0 and the length of the component's text or else
  1305. * an exception is thrown.
  1306. *
  1307. * @param position the position
  1308. * @exception IllegalArgumentException if the value supplied
  1309. * for <code>position</code> is less than zero or greater
  1310. * than the component's text length
  1311. * @beaninfo
  1312. * description: the caret position
  1313. */
  1314. public void setCaretPosition(int position) {
  1315. Document doc = getDocument();
  1316. if (doc != null) {
  1317. if (position > doc.getLength() || position < 0) {
  1318. throw new IllegalArgumentException("bad position: " + position);
  1319. }
  1320. caret.setDot(position);
  1321. }
  1322. }
  1323. /**
  1324. * Returns the position of the text insertion caret for the
  1325. * text component.
  1326. *
  1327. * @return the position of the text insertion caret for the
  1328. * text component >= 0
  1329. */
  1330. public int getCaretPosition() {
  1331. return caret.getDot();
  1332. }
  1333. /**
  1334. * Sets the text of this <code>TextComponent</code>
  1335. * to the specified text. If the text is <code>null</code>
  1336. * or empty, has the effect of simply deleting the old text.
  1337. * When text has been inserted, the resulting caret location
  1338. * is determined by the implementation of the caret class.
  1339. * <p>
  1340. * This method is thread safe, although most Swing methods
  1341. * are not. Please see
  1342. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  1343. * and Swing</A> for more information.
  1344. *
  1345. * Note that text is not a bound property, so no <code>PropertyChangeEvent
  1346. * </code> is fired when it changes. To listen for changes to the text,
  1347. * use <code>DocumentListener</code>.
  1348. *
  1349. * @param t the new text to be set
  1350. * @see #getText
  1351. * @see DefaultCaret
  1352. * @beaninfo
  1353. * description: the text of this component
  1354. */
  1355. public void setText(String t) {
  1356. try {
  1357. Document doc = getDocument();
  1358. if (doc instanceof AbstractDocument) {
  1359. ((AbstractDocument)doc).replace(0, doc.getLength(), t,null);
  1360. }
  1361. else {
  1362. doc.remove(0, doc.getLength());
  1363. doc.insertString(0, t, null);
  1364. }
  1365. } catch (BadLocationException e) {
  1366. UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
  1367. }
  1368. }
  1369. /**
  1370. * Returns the text contained in this <code>TextComponent</code>.
  1371. * If the underlying document is <code>null</code>,
  1372. * will give a <code>NullPointerException</code>.
  1373. *
  1374. * Note that text is not a bound property, so no <code>PropertyChangeEvent
  1375. * </code> is fired when it changes. To listen for changes to the text,
  1376. * use <code>DocumentListener</code>.
  1377. *
  1378. * @return the text
  1379. * @exception NullPointerException if the document is <code>null</code>
  1380. * @see #setText
  1381. */
  1382. public String getText() {
  1383. Document doc = getDocument();
  1384. String txt;
  1385. try {
  1386. txt = doc.getText(0, doc.getLength());
  1387. } catch (BadLocationException e) {
  1388. txt = null;
  1389. }
  1390. return txt;
  1391. }
  1392. /**
  1393. * Returns the selected text contained in this
  1394. * <code>TextComponent</code>. If the selection is
  1395. * <code>null</code> or the document empty, returns <code>null</code>.
  1396. *
  1397. * @return the text
  1398. * @exception IllegalArgumentException if the selection doesn't
  1399. * have a valid mapping into the document for some reason
  1400. * @see #setText
  1401. */
  1402. public String getSelectedText() {
  1403. String txt = null;
  1404. int p0 = Math.min(caret.getDot(), caret.getMark());
  1405. int p1 = Math.max(caret.getDot(), caret.getMark());
  1406. if (p0 != p1) {
  1407. try {
  1408. Document doc = getDocument();
  1409. txt = doc.getText(p0, p1 - p0);
  1410. } catch (BadLocationException e) {
  1411. throw new IllegalArgumentException(e.getMessage());
  1412. }
  1413. }
  1414. return txt;
  1415. }
  1416. /**
  1417. * Returns the boolean indicating whether this
  1418. * <code>TextComponent</code> is editable or not.
  1419. *
  1420. * @return the boolean value
  1421. * @see #setEditable
  1422. */
  1423. public boolean isEditable() {
  1424. return editable;
  1425. }
  1426. /**
  1427. * Sets the specified boolean to indicate whether or not this
  1428. * <code>TextComponent</code> should be editable.
  1429. * A PropertyChange event ("editable") is fired when the
  1430. * state is changed.
  1431. *
  1432. * @param b the boolean to be set
  1433. * @see #isEditable
  1434. * @beaninfo
  1435. * description: specifies if the text can be edited
  1436. * bound: true
  1437. */
  1438. public void setEditable(boolean b) {
  1439. if (b != editable) {
  1440. boolean oldVal = editable;
  1441. editable = b;
  1442. if (editable) {
  1443. setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
  1444. } else {
  1445. setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
  1446. }
  1447. enableInputMethods(editable);
  1448. firePropertyChange("editable", Boolean.valueOf(oldVal), Boolean.valueOf(editable));
  1449. repaint();
  1450. }
  1451. }
  1452. /**
  1453. * Returns the selected text's start position. Return 0 for an
  1454. * empty document, or the value of dot if no selection.
  1455. *
  1456. * @return the start position >= 0
  1457. */
  1458. public int getSelectionStart() {
  1459. int start = Math.min(caret.getDot(), caret.getMark());
  1460. return start;
  1461. }
  1462. /**
  1463. * Sets the selection start to the specified position. The new
  1464. * starting point is constrained to be before or at the current
  1465. * selection end.
  1466. * <p>
  1467. * This is available for backward compatibility to code
  1468. * that called this method on <code>java.awt.TextComponent</code>.
  1469. * This is implemented to forward to the <code>Caret</code>
  1470. * implementation which is where the actual selection is maintained.
  1471. *
  1472. * @param selectionStart the start position of the text >= 0
  1473. * @beaninfo
  1474. * description: starting location of the selection.
  1475. */
  1476. public void setSelectionStart(int selectionStart) {
  1477. /* Route through select method to enforce consistent policy
  1478. * between selectionStart and selectionEnd.
  1479. */
  1480. select(selectionStart, getSelectionEnd());
  1481. }
  1482. /**
  1483. * Returns the selected text's end position. Return 0 if the document
  1484. * is empty, or the value of dot if there is no selection.
  1485. *
  1486. * @return the end position >= 0
  1487. */
  1488. public int getSelectionEnd() {
  1489. int end = Math.max(caret.getDot(), caret.getMark());
  1490. return end;
  1491. }
  1492. /**
  1493. * Sets the selection end to the specified position. The new
  1494. * end point is constrained to be at or after the current
  1495. * selection start.
  1496. * <p>
  1497. * This is available for backward compatibility to code
  1498. * that called this method on <code>java.awt.TextComponent</code>.
  1499. * This is implemented to forward to the <code>Caret</code>
  1500. * implementation which is where the actual selection is maintained.
  1501. *
  1502. * @param selectionEnd the end position of the text >= 0
  1503. * @beaninfo
  1504. * description: ending location of the selection.
  1505. */
  1506. public void setSelectionEnd(int selectionEnd) {
  1507. /* Route through select method to enforce consistent policy
  1508. * between selectionStart and selectionEnd.
  1509. */
  1510. select(getSelectionStart(), selectionEnd);
  1511. }
  1512. /**
  1513. * Selects the text between the specified start and end positions.
  1514. * <p>
  1515. * This method sets the start and end positions of the
  1516. * selected text, enforcing the restriction that the start position
  1517. * must be greater than or equal to zero. The end position must be
  1518. * greater than or equal to the start position, and less than or
  1519. * equal to the length of the text component's text.
  1520. * <p>
  1521. * If the caller supplies values that are inconsistent or out of
  1522. * bounds, the method enforces these constraints silently, and
  1523. * without failure. Specifically, if the start position or end
  1524. * position is greater than the length of the text, it is reset to
  1525. * equal the text length. If the start position is less than zero,
  1526. * it is reset to zero, and if the end position is less than the
  1527. * start position, it is reset to the start position.
  1528. * <p>
  1529. * This call is provided for backward compatibility.
  1530. * It is routed to a call to <code>setCaretPosition</code>
  1531. * followed by a call to <code>moveCaretPosition</code>.
  1532. * The preferred way to manage selection is by calling
  1533. * those methods directly.
  1534. *
  1535. * @param selectionStart the start position of the text
  1536. * @param selectionEnd the end position of the text
  1537. * @see #setCaretPosition
  1538. * @see #moveCaretPosition
  1539. */
  1540. public void select(int selectionStart, int selectionEnd) {
  1541. // argument adjustment done by java.awt.TextComponent
  1542. int docLength = getDocument().getLength();
  1543. if (selectionStart < 0) {
  1544. selectionStart = 0;
  1545. }
  1546. if (selectionStart > docLength) {
  1547. selectionStart = docLength;
  1548. }
  1549. if (selectionEnd > docLength) {
  1550. selectionEnd = docLength;
  1551. }
  1552. if (selectionEnd < selectionStart) {
  1553. selectionEnd = selectionStart;
  1554. }
  1555. setCaretPosition(selectionStart);
  1556. moveCaretPosition(selectionEnd);
  1557. }
  1558. /**
  1559. * Selects all the text in the <code>TextComponent</code>.
  1560. * Does nothing on a <code>null</code> or empty document.
  1561. */
  1562. public void selectAll() {
  1563. Document doc = getDocument();
  1564. if (doc != null) {
  1565. setCaretPosition(0);
  1566. moveCaretPosition(doc.getLength());
  1567. }
  1568. }
  1569. // --- Tooltip Methods ---------------------------------------------
  1570. /**
  1571. * Returns the string to be used as the tooltip for <code>event</code>.
  1572. * This will return one of:
  1573. * <ol>
  1574. * <li>If <code>setToolTipText</code> has been invoked with a
  1575. * non-<code>null</code>
  1576. * value, it will be returned, otherwise
  1577. * <li>The value from invoking <code>getToolTipText</code> on
  1578. * the UI will be returned.
  1579. * </ol>
  1580. * By default <code>JTextComponent</code> does not register
  1581. * itself with the <code>ToolTipManager</code>.
  1582. * This means that tooltips will NOT be shown from the
  1583. * <code>TextUI</code> unless <code>registerComponent</code> has
  1584. * been invoked on the <code>ToolTipManager</code>.
  1585. *
  1586. * @param event the event in question
  1587. * @return the string to be used as the tooltip for <code>event</code>
  1588. * @see javax.swing.JComponent#setToolTipText
  1589. * @see javax.swing.plaf.TextUI#getToolTipText
  1590. * @see javax.swing.ToolTipManager#registerComponent
  1591. */
  1592. public String getToolTipText(MouseEvent event) {
  1593. String retValue = super.getToolTipText(event);
  1594. if (retValue == null) {
  1595. TextUI ui = getUI();
  1596. if (ui != null) {
  1597. retValue = ui.getToolTipText(this, new Point(event.getX(),
  1598. event.getY()));
  1599. }
  1600. }
  1601. return retValue;
  1602. }
  1603. // --- Scrollable methods ---------------------------------------------
  1604. /**
  1605. * Returns the preferred size of the viewport for a view component.
  1606. * This is implemented to do the default behavior of returning
  1607. * the preferred size of the component.
  1608. *
  1609. * @return the <code>preferredSize</code> of a <code>JViewport</code>
  1610. * whose view is this <code>Scrollable</code>
  1611. */
  1612. public Dimension getPreferredScrollableViewportSize() {
  1613. return getPreferredSize();
  1614. }
  1615. /**
  1616. * Components that display logical rows or columns should compute
  1617. * the scroll increment that will completely expose one new row
  1618. * or column, depending on the value of orientation. Ideally,
  1619. * components should handle a partially exposed row or column by
  1620. * returning the distance required to completely expose the item.
  1621. * <p>
  1622. * The default implementation of this is to simply return 10% of
  1623. * the visible area. Subclasses are likely to be able to provide
  1624. * a much more reasonable value.
  1625. *
  1626. * @param visibleRect the view area visible within the viewport
  1627. * @param orientation either <code>SwingConstants.VERTICAL</code> or
  1628. * <code>SwingConstants.HORIZONTAL</code>
  1629. * @param direction less than zero to scroll up/left, greater than
  1630. * zero for down/right
  1631. * @return the "unit" increment for scrolling in the specified direction
  1632. * @exception IllegalArgumentException for an invalid orientation
  1633. * @see JScrollBar#setUnitIncrement
  1634. */
  1635. public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
  1636. switch(orientation) {
  1637. case SwingConstants.VERTICAL:
  1638. return visibleRect.height / 10;
  1639. case SwingConstants.HORIZONTAL:
  1640. return visibleRect.width / 10;
  1641. default:
  1642. throw new IllegalArgumentException("Invalid orientation: " + orientation);
  1643. }
  1644. }
  1645. /**
  1646. * Components that display logical rows or columns should compute
  1647. * the scroll increment that will completely expose one block
  1648. * of rows or columns, depending on the value of orientation.
  1649. * <p>
  1650. * The default implementation of this is to simply return the visible
  1651. * area. Subclasses will likely be able to provide a much more
  1652. * reasonable value.
  1653. *
  1654. * @param visibleRect the view area visible within the viewport
  1655. * @param orientation either <code>SwingConstants.VERTICAL</code> or
  1656. * <code>SwingConstants.HORIZONTAL</code>
  1657. * @param direction less than zero to scroll up/left, greater than zero
  1658. * for down/right
  1659. * @return the "block" increment for scrolling in the specified direction
  1660. * @exception IllegalArgumentException for an invalid orientation
  1661. * @see JScrollBar#setBlockIncrement
  1662. */
  1663. public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
  1664. switch(orientation) {
  1665. case SwingConstants.VERTICAL:
  1666. return visibleRect.height;
  1667. case SwingConstants.HORIZONTAL:
  1668. return visibleRect.width;
  1669. default:
  1670. throw new IllegalArgumentException("Invalid orientation: " + orientation);
  1671. }
  1672. }
  1673. /**
  1674. * Returns true if a viewport should always force the width of this
  1675. * <code>Scrollable</code> to match the width of the viewport.
  1676. * For example a normal text view that supported line wrapping
  1677. * would return true here, since it would be undesirable for
  1678. * wrapped lines to disappear beyond the right
  1679. * edge of the viewport. Note that returning true for a
  1680. * <code>Scrollable</code> whose ancestor is a <code>JScrollPane</code>
  1681. * effectively disables horizontal scrolling.
  1682. * <p>
  1683. * Scrolling containers, like <code>JViewport</code>,
  1684. * will use this method each time they are validated.
  1685. *
  1686. * @return true if a viewport should force the <code>Scrollable</code>s
  1687. * width to match its own
  1688. */
  1689. public boolean getScrollableTracksViewportWidth() {
  1690. if (getParent() instanceof JViewport) {
  1691. return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
  1692. }
  1693. return false;
  1694. }
  1695. /**
  1696. * Returns true if a viewport should always force the height of this
  1697. * <code>Scrollable</code> to match the height of the viewport.
  1698. * For example a columnar text view that flowed text in left to
  1699. * right columns could effectively disable vertical scrolling by
  1700. * returning true here.
  1701. * <p>
  1702. * Scrolling containers, like <code>JViewport</code>,
  1703. * will use this method each time they are validated.
  1704. *
  1705. * @return true if a viewport should force the Scrollables height
  1706. * to match its own
  1707. */
  1708. public boolean getScrollableTracksViewportHeight() {
  1709. if (getParent() instanceof JViewport) {
  1710. return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
  1711. }
  1712. return false;
  1713. }
  1714. /////////////////
  1715. // Accessibility support
  1716. ////////////////
  1717. /**
  1718. * Gets the <code>AccessibleContext</code> associated with this
  1719. * <code>JTextComponent</code>. For text components,
  1720. * the <code>AccessibleContext</code> takes the form of an
  1721. * <code>AccessibleJTextComponent</code>.
  1722. * A new <code>AccessibleJTextComponent</code> instance
  1723. * is created if necessary.
  1724. *
  1725. * @return an <code>AccessibleJTextComponent</code> that serves as the
  1726. * <code>AccessibleContext</code> of this
  1727. * <code>JTextComponent</code>
  1728. */
  1729. public AccessibleContext getAccessibleContext() {
  1730. if (accessibleContext == null) {
  1731. accessibleContext = new AccessibleJTextComponent();
  1732. }
  1733. return accessibleContext;
  1734. }
  1735. /**
  1736. * This class implements accessibility support for the
  1737. * <code>JTextComponent</code> class. It provides an implementation of
  1738. * the Java Accessibility API appropriate to menu user-interface elements.
  1739. * <p>
  1740. * <strong>Warning:</strong>
  1741. * Serialized objects of this class will not be compatible with
  1742. * future Swing releases. The current serialization support is
  1743. * appropriate for short term storage or RMI between applications running
  1744. * the same version of Swing. As of 1.4, support for long term storage
  1745. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  1746. * has been added to the <code>java.beans</code> package.
  1747. * Please see {@link java.beans.XMLEncoder}.
  1748. */
  1749. public class AccessibleJTextComponent extends AccessibleJComponent
  1750. implements AccessibleText, CaretListener, DocumentListener,
  1751. AccessibleAction, AccessibleEditableText {
  1752. int caretPos;
  1753. Point oldLocationOnScreen;
  1754. /**
  1755. * Constructs an AccessibleJTextComponent. Adds a listener to track
  1756. * caret change.
  1757. */
  1758. public AccessibleJTextComponent() {
  1759. Document doc = JTextComponent.this.getDocument();
  1760. if (doc != null) {
  1761. doc.addDocumentListener(this);
  1762. }
  1763. JTextComponent.this.addCaretListener(this);
  1764. caretPos = getCaretPosition();
  1765. try {
  1766. oldLocationOnScreen = getLocationOnScreen();
  1767. } catch (IllegalComponentStateException iae) {
  1768. }
  1769. // Fire a ACCESSIBLE_VISIBLE_DATA_PROPERTY PropertyChangeEvent
  1770. // when the text component moves (e.g., when scrolling).
  1771. // Using an anonymous class since making AccessibleJTextComponent
  1772. // implement ComponentListener would be an API change.
  1773. JTextComponent.this.addComponentListener(new ComponentAdapter() {
  1774. public void componentMoved(ComponentEvent e) {
  1775. try {
  1776. Point newLocationOnScreen = getLocationOnScreen();
  1777. firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  1778. oldLocationOnScreen,
  1779. newLocationOnScreen);
  1780. oldLocationOnScreen = newLocationOnScreen;
  1781. } catch (IllegalComponentStateException iae) {
  1782. }
  1783. }
  1784. });
  1785. }
  1786. /**
  1787. * Handles caret updates (fire appropriate property change event,
  1788. * which are AccessibleContext.ACCESSIBLE_CARET_PROPERTY and
  1789. * AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY).
  1790. * This keeps track of the dot position internally. When the caret
  1791. * moves, the internal position is updated after firing the event.
  1792. *
  1793. * @param e the CaretEvent
  1794. */
  1795. public void caretUpdate(CaretEvent e) {
  1796. int dot = e.getDot();
  1797. int mark = e.getMark();
  1798. if (caretPos != dot) {
  1799. // the caret moved
  1800. firePropertyChange(ACCESSIBLE_CARET_PROPERTY,
  1801. new Integer(caretPos), new Integer(dot));
  1802. caretPos = dot;
  1803. try {
  1804. oldLocationOnScreen = getLocationOnScreen();
  1805. } catch (IllegalComponentStateException iae) {
  1806. }
  1807. }
  1808. if (mark != dot) {
  1809. // there is a selection
  1810. firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
  1811. getSelectedText());
  1812. }
  1813. }
  1814. // DocumentListener methods
  1815. /**
  1816. * Handles document insert (fire appropriate property change event
  1817. * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
  1818. * This tracks the changed offset via the event.
  1819. *
  1820. * @param e the DocumentEvent
  1821. */
  1822. public void insertUpdate(DocumentEvent e) {
  1823. final Integer pos = new Integer (e.getOffset());
  1824. if (SwingUtilities.isEventDispatchThread()) {
  1825. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
  1826. } else {
  1827. Runnable doFire = new Runnable() {
  1828. public void run() {
  1829. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
  1830. null, pos);
  1831. }
  1832. };
  1833. SwingUtilities.invokeLater(doFire);
  1834. }
  1835. }
  1836. /**
  1837. * Handles document remove (fire appropriate property change event,
  1838. * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
  1839. * This tracks the changed offset via the event.
  1840. *
  1841. * @param e the DocumentEvent
  1842. */
  1843. public void removeUpdate(DocumentEvent e) {
  1844. final Integer pos = new Integer (e.getOffset());
  1845. if (SwingUtilities.isEventDispatchThread()) {
  1846. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
  1847. } else {
  1848. Runnable doFire = new Runnable() {
  1849. public void run() {
  1850. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
  1851. null, pos);
  1852. }
  1853. };
  1854. SwingUtilities.invokeLater(doFire);
  1855. }
  1856. }
  1857. /**
  1858. * Handles document remove (fire appropriate property change event,
  1859. * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
  1860. * This tracks the changed offset via the event.
  1861. *
  1862. * @param e the DocumentEvent
  1863. */
  1864. public void changedUpdate(DocumentEvent e) {
  1865. final Integer pos = new Integer (e.getOffset());
  1866. if (SwingUtilities.isEventDispatchThread()) {
  1867. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
  1868. } else {
  1869. Runnable doFire = new Runnable() {
  1870. public void run() {
  1871. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
  1872. null, pos);
  1873. }
  1874. };
  1875. SwingUtilities.invokeLater(doFire);
  1876. }
  1877. }
  1878. /**
  1879. * Gets the state set of the JTextComponent.
  1880. * The AccessibleStateSet of an object is composed of a set of
  1881. * unique AccessibleState's. A change in the AccessibleStateSet
  1882. * of an object will cause a PropertyChangeEvent to be fired
  1883. * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
  1884. *
  1885. * @return an instance of AccessibleStateSet containing the
  1886. * current state set of the object
  1887. * @see AccessibleStateSet
  1888. * @see AccessibleState
  1889. * @see #addPropertyChangeListener
  1890. */
  1891. public AccessibleStateSet getAccessibleStateSet() {
  1892. AccessibleStateSet states = super.getAccessibleStateSet();
  1893. if (JTextComponent.this.isEditable()) {
  1894. states.add(AccessibleState.EDITABLE);
  1895. }
  1896. return states;
  1897. }
  1898. /**
  1899. * Gets the role of this object.
  1900. *
  1901. * @return an instance of AccessibleRole describing the role of the
  1902. * object (AccessibleRole.TEXT)
  1903. * @see AccessibleRole
  1904. */
  1905. public AccessibleRole getAccessibleRole() {
  1906. return AccessibleRole.TEXT;
  1907. }
  1908. /**
  1909. * Get the AccessibleText associated with this object. In the
  1910. * implementation of the Java Accessibility API for this class,
  1911. * return this object, which is responsible for implementing the
  1912. * AccessibleText interface on behalf of itself.
  1913. *
  1914. * @return this object
  1915. */
  1916. public AccessibleText getAccessibleText() {
  1917. return this;
  1918. }
  1919. // --- interface AccessibleText methods ------------------------
  1920. /**
  1921. * Many of these methods are just convenience methods; they
  1922. * just call the equivalent on the parent
  1923. */
  1924. /**
  1925. * Given a point in local coordinates, return the zero-based index
  1926. * of the character under that Point. If the point is invalid,
  1927. * this method returns -1.
  1928. *
  1929. * @param p the Point in local coordinates
  1930. * @return the zero-based index of the character under Point p.
  1931. */
  1932. public int getIndexAtPoint(Point p) {
  1933. if (p == null) {
  1934. return -1;
  1935. }
  1936. return JTextComponent.this.viewToModel(p);
  1937. }
  1938. /**
  1939. * Gets the editor's drawing rectangle. Stolen
  1940. * from the unfortunately named
  1941. * BasicTextUI.getVisibleEditorRect()
  1942. *
  1943. * @return the bounding box for the root view
  1944. */
  1945. Rectangle getRootEditorRect() {
  1946. Rectangle alloc = JTextComponent.this.getBounds();
  1947. if ((alloc.width > 0) && (alloc.height > 0)) {
  1948. alloc.x = alloc.y = 0;
  1949. Insets insets = JTextComponent.this.getInsets();
  1950. alloc.x += insets.left;
  1951. alloc.y += insets.top;
  1952. alloc.width -= insets.left + insets.right;
  1953. alloc.height -= insets.top + insets.bottom;
  1954. return alloc;
  1955. }
  1956. return null;
  1957. }
  1958. /**
  1959. * Determines the bounding box of the character at the given
  1960. * index into the string. The bounds are returned in local
  1961. * coordinates. If the index is invalid a null rectangle
  1962. * is returned.
  1963. *
  1964. * The screen coordinates returned are "unscrolled coordinates"
  1965. * if the JTextComponent is contained in a JScrollPane in which
  1966. * case the resulting rectangle should be composed with the parent
  1967. * coordinates. A good algorithm to use is:
  1968. * <nf>
  1969. * Accessible a:
  1970. * AccessibleText at = a.getAccessibleText();
  1971. * AccessibleComponent ac = a.getAccessibleComponent();
  1972. * Rectangle r = at.getCharacterBounds();
  1973. * Point p = ac.getLocation();
  1974. * r.x += p.x;
  1975. * r.y += p.y;
  1976. * </nf>
  1977. *
  1978. * Note: the JTextComponent must have a valid size (e.g. have
  1979. * been added to a parent container whose ancestor container
  1980. * is a valid top-level window) for this method to be able
  1981. * to return a meaningful (non-null) value.
  1982. *
  1983. * @param i the index into the String >= 0
  1984. * @return the screen coordinates of the character's bounding box
  1985. */
  1986. public Rectangle getCharacterBounds(int i) {
  1987. if (i < 0 || i > model.getLength()-1) {
  1988. return null;
  1989. }
  1990. TextUI ui = getUI();
  1991. if (ui == null) {
  1992. return null;
  1993. }
  1994. Rectangle rect = null;
  1995. Rectangle alloc = getRootEditorRect();
  1996. if (alloc == null) {
  1997. return null;
  1998. }
  1999. if (model instanceof AbstractDocument) {
  2000. ((AbstractDocument)model).readLock();
  2001. }
  2002. try {
  2003. View rootView = ui.getRootView(JTextComponent.this);
  2004. if (rootView != null) {
  2005. rootView.setSize(alloc.width, alloc.height);
  2006. Shape bounds = rootView.modelToView(i,
  2007. Position.Bias.Forward, i+1,
  2008. Position.Bias.Backward, alloc);
  2009. rect = (bounds instanceof Rectangle) ?
  2010. (Rectangle)bounds : bounds.getBounds();
  2011. }
  2012. } catch (BadLocationException e) {
  2013. } finally {
  2014. if (model instanceof AbstractDocument) {
  2015. ((AbstractDocument)model).readUnlock();
  2016. }
  2017. }
  2018. return rect;
  2019. }
  2020. /**
  2021. * Returns the number of characters (valid indices)
  2022. *
  2023. * @return the number of characters >= 0
  2024. */
  2025. public int getCharCount() {
  2026. return model.getLength();
  2027. }
  2028. /**
  2029. * Returns the zero-based offset of the caret.
  2030. *
  2031. * Note: The character to the right of the caret will have the
  2032. * same index value as the offset (the caret is between
  2033. * two characters).
  2034. *
  2035. * @return the zero-based offset of the caret.
  2036. */
  2037. public int getCaretPosition() {
  2038. return JTextComponent.this.getCaretPosition();
  2039. }
  2040. /**
  2041. * Returns the AttributeSet for a given character (at a given index).
  2042. *
  2043. * @param i the zero-based index into the text
  2044. * @return the AttributeSet of the character
  2045. */
  2046. public AttributeSet getCharacterAttribute(int i) {
  2047. Element e = null;
  2048. if (model instanceof AbstractDocument) {
  2049. ((AbstractDocument)model).readLock();
  2050. }
  2051. try {
  2052. for (e = model.getDefaultRootElement(); ! e.isLeaf(); ) {
  2053. int index = e.getElementIndex(i);
  2054. e = e.getElement(index);
  2055. }
  2056. } finally {
  2057. if (model instanceof AbstractDocument) {
  2058. ((AbstractDocument)model).readUnlock();
  2059. }
  2060. }
  2061. return e.getAttributes();
  2062. }
  2063. /**
  2064. * Returns the start offset within the selected text.
  2065. * If there is no selection, but there is
  2066. * a caret, the start and end offsets will be the same.
  2067. * Return 0 if the text is empty, or the caret position
  2068. * if no selection.
  2069. *
  2070. * @return the index into the text of the start of the selection >= 0
  2071. */
  2072. public int getSelectionStart() {
  2073. return JTextComponent.this.getSelectionStart();
  2074. }
  2075. /**
  2076. * Returns the end offset within the selected text.
  2077. * If there is no selection, but there is
  2078. * a caret, the start and end offsets will be the same.
  2079. * Return 0 if the text is empty, or the caret position
  2080. * if no selection.
  2081. *
  2082. * @return the index into teh text of the end of the selection >= 0
  2083. */
  2084. public int getSelectionEnd() {
  2085. return JTextComponent.this.getSelectionEnd();
  2086. }
  2087. /**
  2088. * Returns the portion of the text that is selected.
  2089. *
  2090. * @return the text, null if no selection
  2091. */
  2092. public String getSelectedText() {
  2093. return JTextComponent.this.getSelectedText();
  2094. }
  2095. /**
  2096. * IndexedSegment extends Segment adding the offset into the
  2097. * the model the <code>Segment</code> was asked for.
  2098. */
  2099. private class IndexedSegment extends Segment {
  2100. /**
  2101. * Offset into the model that the position represents.
  2102. */
  2103. public int modelOffset;
  2104. }
  2105. // TIGER - 4170173
  2106. /**
  2107. * Returns the String at a given index. Whitespace
  2108. * between words is treated as a word.
  2109. *
  2110. * @param part the CHARACTER, WORD, or SENTENCE to retrieve
  2111. * @param index an index within the text
  2112. * @return the letter, word, or sentence.
  2113. *
  2114. */
  2115. public String getAtIndex(int part, int index) {
  2116. return getAtIndex(part, index, 0);
  2117. }
  2118. /**
  2119. * Returns the String after a given index. Whitespace
  2120. * between words is treated as a word.
  2121. *
  2122. * @param part the CHARACTER, WORD, or SENTENCE to retrieve
  2123. * @param index an index within the text
  2124. * @return the letter, word, or sentence.
  2125. */
  2126. public String getAfterIndex(int part, int index) {
  2127. return getAtIndex(part, index, 1);
  2128. }
  2129. /**
  2130. * Returns the String before a given index. Whitespace
  2131. * between words is treated a word.
  2132. *
  2133. * @param part the CHARACTER, WORD, or SENTENCE to retrieve
  2134. * @param index an index within the text
  2135. * @return the letter, word, or sentence.
  2136. */
  2137. public String getBeforeIndex(int part, int index) {
  2138. return getAtIndex(part, index, -1);
  2139. }
  2140. /**
  2141. * Gets the word, sentence, or character at <code>index</code>.
  2142. * If <code>direction</code> is non-null this will find the
  2143. * next/previous word/sentence/character.
  2144. */
  2145. private String getAtIndex(int part, int index, int direction) {
  2146. if (model instanceof AbstractDocument) {
  2147. ((AbstractDocument)model).readLock();
  2148. }
  2149. try {
  2150. if (index < 0 || index >= model.getLength()) {
  2151. return null;
  2152. }
  2153. switch (part) {
  2154. case AccessibleText.CHARACTER:
  2155. if (index + direction < model.getLength() &&
  2156. index + direction >= 0) {
  2157. return model.getText(index + direction, 1);
  2158. }
  2159. break;
  2160. case AccessibleText.WORD:
  2161. case AccessibleText.SENTENCE:
  2162. IndexedSegment seg = getSegmentAt(part, index);
  2163. if (seg != null) {
  2164. if (direction != 0) {
  2165. int next;
  2166. if (direction < 0) {
  2167. next = seg.modelOffset - 1;
  2168. }
  2169. else {
  2170. next = seg.modelOffset + direction * seg.count;
  2171. }
  2172. if (next >= 0 && next <= model.getLength()) {
  2173. seg = getSegmentAt(part, next);
  2174. }
  2175. else {
  2176. seg = null;
  2177. }
  2178. }
  2179. if (seg != null) {
  2180. return new String(seg.array, seg.offset,
  2181. seg.count);
  2182. }
  2183. }
  2184. break;
  2185. default:
  2186. break;
  2187. }
  2188. } catch (BadLocationException e) {
  2189. } finally {
  2190. if (model instanceof AbstractDocument) {
  2191. ((AbstractDocument)model).readUnlock();
  2192. }
  2193. }
  2194. return null;
  2195. }
  2196. /*
  2197. * Returns the paragraph element for the specified index.
  2198. */
  2199. private Element getParagraphElement(int index) {
  2200. if (model instanceof PlainDocument ) {
  2201. PlainDocument sdoc = (PlainDocument)model;
  2202. return sdoc.getParagraphElement(index);
  2203. } else if (model instanceof StyledDocument) {
  2204. StyledDocument sdoc = (StyledDocument)model;
  2205. return sdoc.getParagraphElement(index);
  2206. } else {
  2207. Element para = null;
  2208. for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) {
  2209. int pos = para.getElementIndex(index);
  2210. para = para.getElement(pos);
  2211. }
  2212. if (para == null) {
  2213. return null;
  2214. }
  2215. return para.getParentElement();
  2216. }
  2217. }
  2218. /*
  2219. * Returns a <code>Segment</code> containing the paragraph text
  2220. * at <code>index</code>, or null if <code>index</code> isn't
  2221. * valid.
  2222. */
  2223. private IndexedSegment getParagraphElementText(int index)
  2224. throws BadLocationException {
  2225. Element para = getParagraphElement(index);
  2226. if (para != null) {
  2227. IndexedSegment segment = new IndexedSegment();
  2228. try {
  2229. int length = para.getEndOffset() - para.getStartOffset();
  2230. model.getText(para.getStartOffset(), length, segment);
  2231. } catch (BadLocationException e) {
  2232. return null;
  2233. }
  2234. segment.modelOffset = para.getStartOffset();
  2235. return segment;
  2236. }
  2237. return null;
  2238. }
  2239. /**
  2240. * Returns the Segment at <code>index</code> representing either
  2241. * the paragraph or sentence as identified by <code>part</code>, or
  2242. * null if a valid paragraph/sentence can't be found. The offset
  2243. * will point to the start of the word/sentence in the array, and
  2244. * the modelOffset will point to the location of the word/sentence
  2245. * in the model.
  2246. */
  2247. private IndexedSegment getSegmentAt(int part, int index) throws
  2248. BadLocationException {
  2249. IndexedSegment seg = getParagraphElementText(index);
  2250. if (seg == null) {
  2251. return null;
  2252. }
  2253. BreakIterator iterator;
  2254. switch (part) {
  2255. case AccessibleText.WORD:
  2256. iterator = BreakIterator.getWordInstance(getLocale());
  2257. break;
  2258. case AccessibleText.SENTENCE:
  2259. iterator = BreakIterator.getSentenceInstance(getLocale());
  2260. break;
  2261. default:
  2262. return null;
  2263. }
  2264. seg.first();
  2265. iterator.setText(seg);
  2266. int end = iterator.following(index - seg.modelOffset + seg.offset);
  2267. if (end == BreakIterator.DONE) {
  2268. return null;
  2269. }
  2270. if (end > seg.offset + seg.count) {
  2271. return null;
  2272. }
  2273. int begin = iterator.previous();
  2274. if (begin == BreakIterator.DONE ||
  2275. begin >= seg.offset + seg.count) {
  2276. return null;
  2277. }
  2278. seg.modelOffset = seg.modelOffset + begin - seg.offset;
  2279. seg.offset = begin;
  2280. seg.count = end - begin;
  2281. return seg;
  2282. }
  2283. // begin AccessibleEditableText methods -----
  2284. /**
  2285. * Returns the AccessibleEditableText interface for
  2286. * this text component.
  2287. *
  2288. * @return the AccessibleEditableText interface
  2289. */
  2290. public AccessibleEditableText getAccessibleEditableText() {
  2291. return this;
  2292. }
  2293. /**
  2294. * Sets the text contents to the specified string.
  2295. *
  2296. * @param s the string to set the text contents
  2297. */
  2298. public void setTextContents(String s) {
  2299. JTextComponent.this.setText(s);
  2300. }
  2301. /**
  2302. * Inserts the specified string at the given index
  2303. *
  2304. * @param index the index in the text where the string will
  2305. * be inserted
  2306. * @param s the string to insert in the text
  2307. */
  2308. public void insertTextAtIndex(int index, String s) {
  2309. Document doc = JTextComponent.this.getDocument();
  2310. if (doc != null) {
  2311. try {
  2312. if (s != null && s.length() > 0) {
  2313. boolean composedTextSaved = saveComposedText(index);
  2314. doc.insertString(index, s, null);
  2315. if (composedTextSaved) {
  2316. restoreComposedText();
  2317. }
  2318. }
  2319. } catch (BadLocationException e) {
  2320. UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
  2321. }
  2322. }
  2323. }
  2324. /**
  2325. * Returns the text string between two indices.
  2326. *
  2327. * @param startIndex the starting index in the text
  2328. * @param endIndex the ending index in the text
  2329. * @return the text string between the indices
  2330. */
  2331. public String getTextRange(int startIndex, int endIndex) {
  2332. String txt = null;
  2333. int p0 = Math.min(startIndex, endIndex);
  2334. int p1 = Math.max(startIndex, endIndex);
  2335. if (p0 != p1) {
  2336. try {
  2337. Document doc = JTextComponent.this.getDocument();
  2338. txt = doc.getText(p0, p1 - p0);
  2339. } catch (BadLocationException e) {
  2340. throw new IllegalArgumentException(e.getMessage());
  2341. }
  2342. }
  2343. return txt;
  2344. }
  2345. /**
  2346. * Deletes the text between two indices
  2347. *
  2348. * @param startIndex the starting index in the text
  2349. * @param endIndex the ending index in the text
  2350. */
  2351. public void delete(int startIndex, int endIndex) {
  2352. if (isEditable() && isEnabled()) {
  2353. try {
  2354. int p0 = Math.min(startIndex, endIndex);
  2355. int p1 = Math.max(startIndex, endIndex);
  2356. if (p0 != p1) {
  2357. Document doc = getDocument();
  2358. doc.remove(p0, p1 - p0);
  2359. }
  2360. } catch (BadLocationException e) {
  2361. }
  2362. } else {
  2363. UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
  2364. }
  2365. }
  2366. /**
  2367. * Cuts the text between two indices into the system clipboard.
  2368. *
  2369. * @param startIndex the starting index in the text
  2370. * @param endIndex the ending index in the text
  2371. */
  2372. public void cut(int startIndex, int endIndex) {
  2373. selectText(startIndex, endIndex);
  2374. JTextComponent.this.cut();
  2375. }
  2376. /**
  2377. * Pastes the text from the system clipboard into the text
  2378. * starting at the specified index.
  2379. *
  2380. * @param startIndex the starting index in the text
  2381. */
  2382. public void paste(int startIndex) {
  2383. setCaretPosition(startIndex);
  2384. JTextComponent.this.paste();
  2385. }
  2386. /**
  2387. * Replaces the text between two indices with the specified
  2388. * string.
  2389. *
  2390. * @param startIndex the starting index in the text
  2391. * @param endIndex the ending index in the text
  2392. * @param s the string to replace the text between two indices
  2393. */
  2394. public void replaceText(int startIndex, int endIndex, String s) {
  2395. selectText(startIndex, endIndex);
  2396. JTextComponent.this.replaceSelection(s);
  2397. }
  2398. /**
  2399. * Selects the text between two indices.
  2400. *
  2401. * @param startIndex the starting index in the text
  2402. * @param endIndex the ending index in the text
  2403. */
  2404. public void selectText(int startIndex, int endIndex) {
  2405. JTextComponent.this.select(startIndex, endIndex);
  2406. }
  2407. /**
  2408. * Sets attributes for the text between two indices.
  2409. *
  2410. * @param startIndex the starting index in the text
  2411. * @param endIndex the ending index in the text
  2412. * @param as the attribute set
  2413. * @see AttributeSet
  2414. */
  2415. public void setAttributes(int startIndex, int endIndex,
  2416. AttributeSet as) {
  2417. // Fixes bug 4487492
  2418. Document doc = JTextComponent.this.getDocument();
  2419. if (doc != null && doc instanceof StyledDocument) {
  2420. StyledDocument sDoc = (StyledDocument)doc;
  2421. int offset = startIndex;
  2422. int length = endIndex - startIndex;
  2423. sDoc.setCharacterAttributes(offset, length, as, true);
  2424. }
  2425. }
  2426. // ----- end AccessibleEditableText methods
  2427. // --- interface AccessibleAction methods ------------------------
  2428. public AccessibleAction getAccessibleAction() {
  2429. return this;
  2430. }
  2431. /**
  2432. * Returns the number of accessible actions available in this object
  2433. * If there are more than one, the first one is considered the
  2434. * "default" action of the object.
  2435. *
  2436. * @return the zero-based number of Actions in this object
  2437. */
  2438. public int getAccessibleActionCount() {
  2439. Action [] actions = JTextComponent.this.getActions();
  2440. return actions.length;
  2441. }
  2442. /**
  2443. * Returns a description of the specified action of the object.
  2444. *
  2445. * @param i zero-based index of the actions
  2446. * @return a String description of the action
  2447. * @see #getAccessibleActionCount
  2448. */
  2449. public String getAccessibleActionDescription(int i) {
  2450. Action [] actions = JTextComponent.this.getActions();
  2451. if (i < 0 || i >= actions.length) {
  2452. return null;
  2453. }
  2454. return (String)actions[i].getValue(Action.NAME);
  2455. }
  2456. /**
  2457. * Performs the specified Action on the object
  2458. *
  2459. * @param i zero-based index of actions
  2460. * @return true if the action was performed; otherwise false.
  2461. * @see #getAccessibleActionCount
  2462. */
  2463. public boolean doAccessibleAction(int i) {
  2464. Action [] actions = JTextComponent.this.getActions();
  2465. if (i < 0 || i >= actions.length) {
  2466. return false;
  2467. }
  2468. ActionEvent ae =
  2469. new ActionEvent(JTextComponent.this,
  2470. ActionEvent.ACTION_PERFORMED, null,
  2471. EventQueue.getMostRecentEventTime(),
  2472. getCurrentEventModifiers());
  2473. actions[i].actionPerformed(ae);
  2474. return true;
  2475. }
  2476. // ----- end AccessibleAction methods
  2477. }
  2478. // --- serialization ---------------------------------------------
  2479. private void readObject(ObjectInputStream s)
  2480. throws IOException, ClassNotFoundException
  2481. {
  2482. s.defaultReadObject();
  2483. caretEvent = new MutableCaretEvent(this);
  2484. addMouseListener(caretEvent);
  2485. addFocusListener(caretEvent);
  2486. }
  2487. // --- member variables ----------------------------------
  2488. /**
  2489. * The document model.
  2490. */
  2491. private Document model;
  2492. /**
  2493. * The caret used to display the insert position
  2494. * and navigate throughout the document.
  2495. *
  2496. * PENDING(prinz)
  2497. * This should be serializable, default installed
  2498. * by UI.
  2499. */
  2500. private transient Caret caret;
  2501. /**
  2502. * Object responsible for restricting the cursor navigation.
  2503. */
  2504. private NavigationFilter navigationFilter;
  2505. /**
  2506. * The object responsible for managing highlights.
  2507. *
  2508. * PENDING(prinz)
  2509. * This should be serializable, default installed
  2510. * by UI.
  2511. */
  2512. private transient Highlighter highlighter;
  2513. /**
  2514. * The current key bindings in effect.
  2515. *
  2516. * PENDING(prinz)
  2517. * This should be serializable, default installed
  2518. * by UI.
  2519. */
  2520. private transient Keymap keymap;
  2521. private transient MutableCaretEvent caretEvent;
  2522. private Color caretColor;
  2523. private Color selectionColor;
  2524. private Color selectedTextColor;
  2525. private Color disabledTextColor;
  2526. private boolean editable;
  2527. private Insets margin;
  2528. private char focusAccelerator;
  2529. private boolean dragEnabled;
  2530. /**
  2531. * TransferHandler used if one hasn't been supplied by the UI.
  2532. */
  2533. private static DefaultTransferHandler defaultTransferHandler;
  2534. /**
  2535. * Maps from class name to Boolean indicating if
  2536. * <code>processInputMethodEvent</code> has been overriden.
  2537. */
  2538. private static Map overrideMap;
  2539. /**
  2540. * Returns a string representation of this <code>JTextComponent</code>.
  2541. * This method is intended to be used only for debugging purposes, and the
  2542. * content and format of the returned string may vary between
  2543. * implementations. The returned string may be empty but may not
  2544. * be <code>null</code>.
  2545. * <P>
  2546. * Overriding <code>paramString</code> to provide information about the
  2547. * specific new aspects of the JFC components.
  2548. *
  2549. * @return a string representation of this <code>JTextComponent</code>
  2550. */
  2551. protected String paramString() {
  2552. String editableString = (editable ?
  2553. "true" : "false");
  2554. String caretColorString = (caretColor != null ?
  2555. caretColor.toString() : "");
  2556. String selectionColorString = (selectionColor != null ?
  2557. selectionColor.toString() : "");
  2558. String selectedTextColorString = (selectedTextColor != null ?
  2559. selectedTextColor.toString() : "");
  2560. String disabledTextColorString = (disabledTextColor != null ?
  2561. disabledTextColor.toString() : "");
  2562. String marginString = (margin != null ?
  2563. margin.toString() : "");
  2564. return super.paramString() +
  2565. ",caretColor=" + caretColorString +
  2566. ",disabledTextColor=" + disabledTextColorString +
  2567. ",editable=" + editableString +
  2568. ",margin=" + marginString +
  2569. ",selectedTextColor=" + selectedTextColorString +
  2570. ",selectionColor=" + selectionColorString;
  2571. }
  2572. /**
  2573. * A Simple TransferHandler that exports the data as a String, and
  2574. * imports the data from the String clipboard. This is only used
  2575. * if the UI hasn't supplied one, which would only happen if someone
  2576. * hasn't subclassed Basic.
  2577. */
  2578. static class DefaultTransferHandler extends TransferHandler implements
  2579. UIResource {
  2580. public void exportToClipboard(JComponent comp, Clipboard clipboard,
  2581. int action) throws IllegalStateException {
  2582. if (comp instanceof JTextComponent) {
  2583. JTextComponent text = (JTextComponent)comp;
  2584. int p0 = text.getSelectionStart();
  2585. int p1 = text.getSelectionEnd();
  2586. if (p0 != p1) {
  2587. try {
  2588. Document doc = text.getDocument();
  2589. String srcData = doc.getText(p0, p1 - p0);
  2590. StringSelection contents =new StringSelection(srcData);
  2591. // this may throw an IllegalStateException,
  2592. // but it will be caught and handled in the
  2593. // action that invoked this method
  2594. clipboard.setContents(contents, null);
  2595. if (action == TransferHandler.MOVE) {
  2596. doc.remove(p0, p1 - p0);
  2597. }
  2598. } catch (BadLocationException ble) {}
  2599. }
  2600. }
  2601. }
  2602. public boolean importData(JComponent comp, Transferable t) {
  2603. if (comp instanceof JTextComponent) {
  2604. DataFlavor flavor = getFlavor(t.getTransferDataFlavors());
  2605. if (flavor != null) {
  2606. InputContext ic = comp.getInputContext();
  2607. if (ic != null) {
  2608. ic.endComposition();
  2609. }
  2610. try {
  2611. String data = (String)t.getTransferData(flavor);
  2612. ((JTextComponent)comp).replaceSelection(data);
  2613. return true;
  2614. } catch (UnsupportedFlavorException ufe) {
  2615. } catch (IOException ioe) {
  2616. }
  2617. }
  2618. }
  2619. return false;
  2620. }
  2621. public boolean canImport(JComponent comp,
  2622. DataFlavor[] transferFlavors) {
  2623. JTextComponent c = (JTextComponent)comp;
  2624. if (!(c.isEditable() && c.isEnabled())) {
  2625. return false;
  2626. }
  2627. return (getFlavor(transferFlavors) != null);
  2628. }
  2629. public int getSourceActions(JComponent c) {
  2630. return NONE;
  2631. }
  2632. private DataFlavor getFlavor(DataFlavor[] flavors) {
  2633. if (flavors != null) {
  2634. for (int counter = 0; counter < flavors.length; counter++) {
  2635. if (flavors[counter].equals(DataFlavor.stringFlavor)) {
  2636. return flavors[counter];
  2637. }
  2638. }
  2639. }
  2640. return null;
  2641. }
  2642. }
  2643. /**
  2644. * Returns the JTextComponent that most recently had focus. The returned
  2645. * value may currently have focus.
  2646. */
  2647. static final JTextComponent getFocusedComponent() {
  2648. return (JTextComponent)AppContext.getAppContext().
  2649. get(FOCUSED_COMPONENT);
  2650. }
  2651. private int getCurrentEventModifiers() {
  2652. int modifiers = 0;
  2653. AWTEvent currentEvent = EventQueue.getCurrentEvent();
  2654. if (currentEvent instanceof InputEvent) {
  2655. modifiers = ((InputEvent)currentEvent).getModifiers();
  2656. } else if (currentEvent instanceof ActionEvent) {
  2657. modifiers = ((ActionEvent)currentEvent).getModifiers();
  2658. }
  2659. return modifiers;
  2660. }
  2661. private static final Object KEYMAP_TABLE =
  2662. new StringBuilder("JTextComponent_KeymapTable");
  2663. private JTextComponent editor;
  2664. //
  2665. // member variables used for on-the-spot input method
  2666. // editing style support
  2667. //
  2668. private transient InputMethodRequests inputMethodRequestsHandler;
  2669. private SimpleAttributeSet composedTextAttribute;
  2670. private String composedTextContent;
  2671. private Position composedTextStart;
  2672. private Position composedTextEnd;
  2673. private Position latestCommittedTextStart;
  2674. private Position latestCommittedTextEnd;
  2675. private ComposedTextCaret composedTextCaret;
  2676. private transient Caret originalCaret;
  2677. /**
  2678. * Set to true after the check for the override of processInputMethodEvent
  2679. * has been checked.
  2680. */
  2681. private boolean checkedInputOverride;
  2682. private boolean needToSendKeyTypedEvent;
  2683. static class DefaultKeymap implements Keymap {
  2684. DefaultKeymap(String nm, Keymap parent) {
  2685. this.nm = nm;
  2686. this.parent = parent;
  2687. bindings = new Hashtable();
  2688. }
  2689. /**
  2690. * Fetch the default action to fire if a
  2691. * key is typed (ie a KEY_TYPED KeyEvent is received)
  2692. * and there is no binding for it. Typically this
  2693. * would be some action that inserts text so that
  2694. * the keymap doesn't require an action for each
  2695. * possible key.
  2696. */
  2697. public Action getDefaultAction() {
  2698. if (defaultAction != null) {
  2699. return defaultAction;
  2700. }
  2701. return (parent != null) ? parent.getDefaultAction() : null;
  2702. }
  2703. /**
  2704. * Set the default action to fire if a key is typed.
  2705. */
  2706. public void setDefaultAction(Action a) {
  2707. defaultAction = a;
  2708. }
  2709. public String getName() {
  2710. return nm;
  2711. }
  2712. public Action getAction(KeyStroke key) {
  2713. Action a = (Action) bindings.get(key);
  2714. if ((a == null) && (parent != null)) {
  2715. a = parent.getAction(key);
  2716. }
  2717. return a;
  2718. }
  2719. public KeyStroke[] getBoundKeyStrokes() {
  2720. KeyStroke[] keys = new KeyStroke[bindings.size()];
  2721. int i = 0;
  2722. for (Enumeration e = bindings.keys() ; e.hasMoreElements() ;) {
  2723. keys[i++] = (KeyStroke) e.nextElement();
  2724. }
  2725. return keys;
  2726. }
  2727. public Action[] getBoundActions() {
  2728. Action[] actions = new Action[bindings.size()];
  2729. int i = 0;
  2730. for (Enumeration e = bindings.elements() ; e.hasMoreElements() ;) {
  2731. actions[i++] = (Action) e.nextElement();
  2732. }
  2733. return actions;
  2734. }
  2735. public KeyStroke[] getKeyStrokesForAction(Action a) {
  2736. if (a == null) {
  2737. return null;
  2738. }
  2739. KeyStroke[] retValue = null;
  2740. // Determine local bindings first.
  2741. Vector keyStrokes = null;
  2742. for (Enumeration enum_ = bindings.keys();
  2743. enum_.hasMoreElements();) {
  2744. Object key = enum_.nextElement();
  2745. if (bindings.get(key) == a) {
  2746. if (keyStrokes == null) {
  2747. keyStrokes = new Vector();
  2748. }
  2749. keyStrokes.addElement(key);
  2750. }
  2751. }
  2752. // See if the parent has any.
  2753. if (parent != null) {
  2754. KeyStroke[] pStrokes = parent.getKeyStrokesForAction(a);
  2755. if (pStrokes != null) {
  2756. // Remove any bindings defined in the parent that
  2757. // are locally defined.
  2758. int rCount = 0;
  2759. for (int counter = pStrokes.length - 1; counter >= 0;
  2760. counter--) {
  2761. if (isLocallyDefined(pStrokes[counter])) {
  2762. pStrokes[counter] = null;
  2763. rCount++;
  2764. }
  2765. }
  2766. if (rCount > 0 && rCount < pStrokes.length) {
  2767. if (keyStrokes == null) {
  2768. keyStrokes = new Vector();
  2769. }
  2770. for (int counter = pStrokes.length - 1; counter >= 0;
  2771. counter--) {
  2772. if (pStrokes[counter] != null) {
  2773. keyStrokes.addElement(pStrokes[counter]);
  2774. }
  2775. }
  2776. }
  2777. else if (rCount == 0) {
  2778. if (keyStrokes == null) {
  2779. retValue = pStrokes;
  2780. }
  2781. else {
  2782. retValue = new KeyStroke[keyStrokes.size() +
  2783. pStrokes.length];
  2784. keyStrokes.copyInto(retValue);
  2785. System.arraycopy(pStrokes, 0, retValue,
  2786. keyStrokes.size(), pStrokes.length);
  2787. keyStrokes = null;
  2788. }
  2789. }
  2790. }
  2791. }
  2792. if (keyStrokes != null) {
  2793. retValue = new KeyStroke[keyStrokes.size()];
  2794. keyStrokes.copyInto(retValue);
  2795. }
  2796. return retValue;
  2797. }
  2798. public boolean isLocallyDefined(KeyStroke key) {
  2799. return bindings.containsKey(key);
  2800. }
  2801. public void addActionForKeyStroke(KeyStroke key, Action a) {
  2802. bindings.put(key, a);
  2803. }
  2804. public void removeKeyStrokeBinding(KeyStroke key) {
  2805. bindings.remove(key);
  2806. }
  2807. public void removeBindings() {
  2808. bindings.clear();
  2809. }
  2810. public Keymap getResolveParent() {
  2811. return parent;
  2812. }
  2813. public void setResolveParent(Keymap parent) {
  2814. this.parent = parent;
  2815. }
  2816. /**
  2817. * String representation of the keymap... potentially
  2818. * a very long string.
  2819. */
  2820. public String toString() {
  2821. return "Keymap[" + nm + "]" + bindings;
  2822. }
  2823. String nm;
  2824. Keymap parent;
  2825. Hashtable bindings;
  2826. Action defaultAction;
  2827. }
  2828. /**
  2829. * KeymapWrapper wraps a Keymap inside an InputMap. For KeymapWrapper
  2830. * to be useful it must be used with a KeymapActionMap.
  2831. * KeymapWrapper for the most part, is an InputMap with two parents.
  2832. * The first parent visited is ALWAYS the Keymap, with the second
  2833. * parent being the parent inherited from InputMap. If
  2834. * <code>keymap.getAction</code> returns null, implying the Keymap
  2835. * does not have a binding for the KeyStroke,
  2836. * the parent is then visited. If the Keymap has a binding, the
  2837. * Action is returned, if not and the KeyStroke represents a
  2838. * KeyTyped event and the Keymap has a defaultAction,
  2839. * <code>DefaultActionKey</code> is returned.
  2840. * <p>KeymapActionMap is then able to transate the object passed in
  2841. * to either message the Keymap, or message its default implementation.
  2842. */
  2843. static class KeymapWrapper extends InputMap {
  2844. static final Object DefaultActionKey = new Object();
  2845. private Keymap keymap;
  2846. KeymapWrapper(Keymap keymap) {
  2847. this.keymap = keymap;
  2848. }
  2849. public KeyStroke[] keys() {
  2850. KeyStroke[] sKeys = super.keys();
  2851. KeyStroke[] keymapKeys = keymap.getBoundKeyStrokes();
  2852. int sCount = (sKeys == null) ? 0 : sKeys.length;
  2853. int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
  2854. if (sCount == 0) {
  2855. return keymapKeys;
  2856. }
  2857. if (keymapCount == 0) {
  2858. return sKeys;
  2859. }
  2860. KeyStroke[] retValue = new KeyStroke[sCount + keymapCount];
  2861. // There may be some duplication here...
  2862. System.arraycopy(sKeys, 0, retValue, 0, sCount);
  2863. System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
  2864. return retValue;
  2865. }
  2866. public int size() {
  2867. // There may be some duplication here...
  2868. KeyStroke[] keymapStrokes = keymap.getBoundKeyStrokes();
  2869. int keymapCount = (keymapStrokes == null) ? 0:
  2870. keymapStrokes.length;
  2871. return super.size() + keymapCount;
  2872. }
  2873. public Object get(KeyStroke keyStroke) {
  2874. Object retValue = keymap.getAction(keyStroke);
  2875. if (retValue == null) {
  2876. retValue = super.get(keyStroke);
  2877. if (retValue == null &&
  2878. keyStroke.getKeyChar() != KeyEvent.CHAR_UNDEFINED &&
  2879. keymap.getDefaultAction() != null) {
  2880. // Implies this is a KeyTyped event, use the default
  2881. // action.
  2882. retValue = DefaultActionKey;
  2883. }
  2884. }
  2885. return retValue;
  2886. }
  2887. }
  2888. /**
  2889. * Wraps a Keymap inside an ActionMap. This is used with
  2890. * a KeymapWrapper. If <code>get</code> is passed in
  2891. * <code>KeymapWrapper.DefaultActionKey</code>, the default action is
  2892. * returned, otherwise if the key is an Action, it is returned.
  2893. */
  2894. static class KeymapActionMap extends ActionMap {
  2895. private Keymap keymap;
  2896. KeymapActionMap(Keymap keymap) {
  2897. this.keymap = keymap;
  2898. }
  2899. public Object[] keys() {
  2900. Object[] sKeys = super.keys();
  2901. Object[] keymapKeys = keymap.getBoundActions();
  2902. int sCount = (sKeys == null) ? 0 : sKeys.length;
  2903. int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
  2904. boolean hasDefault = (keymap.getDefaultAction() != null);
  2905. if (hasDefault) {
  2906. keymapCount++;
  2907. }
  2908. if (sCount == 0) {
  2909. if (hasDefault) {
  2910. Object[] retValue = new Object[keymapCount];
  2911. if (keymapCount > 1) {
  2912. System.arraycopy(keymapKeys, 0, retValue, 0,
  2913. keymapCount - 1);
  2914. }
  2915. retValue[keymapCount - 1] = KeymapWrapper.DefaultActionKey;
  2916. return retValue;
  2917. }
  2918. return keymapKeys;
  2919. }
  2920. if (keymapCount == 0) {
  2921. return sKeys;
  2922. }
  2923. Object[] retValue = new Object[sCount + keymapCount];
  2924. // There may be some duplication here...
  2925. System.arraycopy(sKeys, 0, retValue, 0, sCount);
  2926. if (hasDefault) {
  2927. if (keymapCount > 1) {
  2928. System.arraycopy(keymapKeys, 0, retValue, sCount,
  2929. keymapCount - 1);
  2930. }
  2931. retValue[sCount + keymapCount - 1] = KeymapWrapper.
  2932. DefaultActionKey;
  2933. }
  2934. else {
  2935. System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
  2936. }
  2937. return retValue;
  2938. }
  2939. public int size() {
  2940. // There may be some duplication here...
  2941. Object[] actions = keymap.getBoundActions();
  2942. int keymapCount = (actions == null) ? 0 : actions.length;
  2943. if (keymap.getDefaultAction() != null) {
  2944. keymapCount++;
  2945. }
  2946. return super.size() + keymapCount;
  2947. }
  2948. public Action get(Object key) {
  2949. Action retValue = super.get(key);
  2950. if (retValue == null) {
  2951. // Try the Keymap.
  2952. if (key == KeymapWrapper.DefaultActionKey) {
  2953. retValue = keymap.getDefaultAction();
  2954. }
  2955. else if (key instanceof Action) {
  2956. // This is a little iffy, technically an Action is
  2957. // a valid Key. We're assuming the Action came from
  2958. // the InputMap though.
  2959. retValue = (Action)key;
  2960. }
  2961. }
  2962. return retValue;
  2963. }
  2964. }
  2965. private static final Object FOCUSED_COMPONENT =
  2966. new StringBuilder("JTextComponent_FocusedComponent");
  2967. /**
  2968. * The default keymap that will be shared by all
  2969. * <code>JTextComponent</code> instances unless they
  2970. * have had a different keymap set.
  2971. */
  2972. public static final String DEFAULT_KEYMAP = "default";
  2973. /**
  2974. * Event to use when firing a notification of change to caret
  2975. * position. This is mutable so that the event can be reused
  2976. * since caret events can be fairly high in bandwidth.
  2977. */
  2978. static class MutableCaretEvent extends CaretEvent implements ChangeListener, FocusListener, MouseListener {
  2979. MutableCaretEvent(JTextComponent c) {
  2980. super(c);
  2981. }
  2982. final void fire() {
  2983. JTextComponent c = (JTextComponent) getSource();
  2984. if (c != null) {
  2985. Caret caret = c.getCaret();
  2986. dot = caret.getDot();
  2987. mark = caret.getMark();
  2988. c.fireCaretUpdate(this);
  2989. }
  2990. }
  2991. public final String toString() {
  2992. return "dot=" + dot + "," + "mark=" + mark;
  2993. }
  2994. // --- CaretEvent methods -----------------------
  2995. public final int getDot() {
  2996. return dot;
  2997. }
  2998. public final int getMark() {
  2999. return mark;
  3000. }
  3001. // --- ChangeListener methods -------------------
  3002. public final void stateChanged(ChangeEvent e) {
  3003. if (! dragActive) {
  3004. fire();
  3005. }
  3006. }
  3007. // --- FocusListener methods -----------------------------------
  3008. public void focusGained(FocusEvent fe) {
  3009. AppContext.getAppContext().put(FOCUSED_COMPONENT,
  3010. fe.getSource());
  3011. }
  3012. public void focusLost(FocusEvent fe) {
  3013. }
  3014. // --- MouseListener methods -----------------------------------
  3015. /**
  3016. * Requests focus on the associated
  3017. * text component, and try to set the cursor position.
  3018. *
  3019. * @param e the mouse event
  3020. * @see MouseListener#mousePressed
  3021. */
  3022. public final void mousePressed(MouseEvent e) {
  3023. dragActive = true;
  3024. }
  3025. /**
  3026. * Called when the mouse is released.
  3027. *
  3028. * @param e the mouse event
  3029. * @see MouseListener#mouseReleased
  3030. */
  3031. public final void mouseReleased(MouseEvent e) {
  3032. dragActive = false;
  3033. fire();
  3034. }
  3035. public final void mouseClicked(MouseEvent e) {
  3036. }
  3037. public final void mouseEntered(MouseEvent e) {
  3038. }
  3039. public final void mouseExited(MouseEvent e) {
  3040. }
  3041. private boolean dragActive;
  3042. private int dot;
  3043. private int mark;
  3044. }
  3045. //
  3046. // Process any input method events that the component itself
  3047. // recognizes. The default on-the-spot handling for input method
  3048. // composed(uncommitted) text is done here after all input
  3049. // method listeners get called for stealing the events.
  3050. //
  3051. protected void processInputMethodEvent(InputMethodEvent e) {
  3052. // let listeners handle the events
  3053. super.processInputMethodEvent(e);
  3054. if (!e.isConsumed()) {
  3055. if (! isEditable()) {
  3056. return;
  3057. } else {
  3058. switch (e.getID()) {
  3059. case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED:
  3060. replaceInputMethodText(e);
  3061. // fall through
  3062. case InputMethodEvent.CARET_POSITION_CHANGED:
  3063. setInputMethodCaretPosition(e);
  3064. break;
  3065. }
  3066. }
  3067. e.consume();
  3068. }
  3069. }
  3070. //
  3071. // Overrides this method to become an active input method client.
  3072. //
  3073. public InputMethodRequests getInputMethodRequests() {
  3074. if (inputMethodRequestsHandler == null) {
  3075. inputMethodRequestsHandler =
  3076. (InputMethodRequests)new InputMethodRequestsHandler();
  3077. Document doc = getDocument();
  3078. if (doc != null) {
  3079. doc.addDocumentListener((DocumentListener)inputMethodRequestsHandler);
  3080. }
  3081. }
  3082. return inputMethodRequestsHandler;
  3083. }
  3084. //
  3085. // Overrides this method to watch the listener installed.
  3086. //
  3087. public void addInputMethodListener(InputMethodListener l) {
  3088. super.addInputMethodListener(l);
  3089. if (l != null) {
  3090. needToSendKeyTypedEvent = false;
  3091. checkedInputOverride = true;
  3092. }
  3093. }
  3094. //
  3095. // Default implementation of the InputMethodRequests interface.
  3096. //
  3097. class InputMethodRequestsHandler implements InputMethodRequests, DocumentListener {
  3098. // --- InputMethodRequests methods ---
  3099. public AttributedCharacterIterator cancelLatestCommittedText(
  3100. Attribute[] attributes) {
  3101. Document doc = getDocument();
  3102. if ((doc != null) && (latestCommittedTextStart != null)
  3103. && (!latestCommittedTextStart.equals(latestCommittedTextEnd))) {
  3104. try {
  3105. int startIndex = latestCommittedTextStart.getOffset();
  3106. int endIndex = latestCommittedTextEnd.getOffset();
  3107. String latestCommittedText =
  3108. doc.getText(startIndex, endIndex - startIndex);
  3109. doc.remove(startIndex, endIndex - startIndex);
  3110. return new AttributedString(latestCommittedText).getIterator();
  3111. } catch (BadLocationException ble) {}
  3112. }
  3113. return null;
  3114. }
  3115. public AttributedCharacterIterator getCommittedText(int beginIndex,
  3116. int endIndex, Attribute[] attributes) {
  3117. int composedStartIndex = 0;
  3118. int composedEndIndex = 0;
  3119. if (composedTextExists()) {
  3120. composedStartIndex = composedTextStart.getOffset();
  3121. composedEndIndex = composedTextEnd.getOffset();
  3122. }
  3123. String committed;
  3124. try {
  3125. if (beginIndex < composedStartIndex) {
  3126. if (endIndex <= composedStartIndex) {
  3127. committed = getText(beginIndex, endIndex - beginIndex);
  3128. } else {
  3129. int firstPartLength = composedStartIndex - beginIndex;
  3130. committed = getText(beginIndex, firstPartLength) +
  3131. getText(composedEndIndex, endIndex - beginIndex - firstPartLength);
  3132. }
  3133. } else {
  3134. committed = getText(beginIndex + (composedEndIndex - composedStartIndex),
  3135. endIndex - beginIndex);
  3136. }
  3137. } catch (BadLocationException ble) {
  3138. throw new IllegalArgumentException("Invalid range");
  3139. }
  3140. return new AttributedString(committed).getIterator();
  3141. }
  3142. public int getCommittedTextLength() {
  3143. Document doc = getDocument();
  3144. int length = 0;
  3145. if (doc != null) {
  3146. length = doc.getLength();
  3147. if (composedTextContent != null) {
  3148. length -= composedTextEnd.getOffset() -
  3149. composedTextStart.getOffset();
  3150. }
  3151. }
  3152. return length;
  3153. }
  3154. public int getInsertPositionOffset() {
  3155. int composedStartIndex = 0;
  3156. int composedEndIndex = 0;
  3157. if (composedTextExists()) {
  3158. composedStartIndex = composedTextStart.getOffset();
  3159. composedEndIndex = composedTextEnd.getOffset();
  3160. }
  3161. int caretIndex = getCaretPosition();
  3162. if (caretIndex < composedStartIndex) {
  3163. return caretIndex;
  3164. } else if (caretIndex < composedEndIndex) {
  3165. return composedStartIndex;
  3166. } else {
  3167. return caretIndex - (composedEndIndex - composedStartIndex);
  3168. }
  3169. }
  3170. public TextHitInfo getLocationOffset(int x, int y) {
  3171. if (composedTextAttribute == null) {
  3172. return null;
  3173. } else {
  3174. Point p = getLocationOnScreen();
  3175. p.x = x - p.x;
  3176. p.y = y - p.y;
  3177. int pos = viewToModel(p);
  3178. if ((pos >= composedTextStart.getOffset()) &&
  3179. (pos <= composedTextEnd.getOffset())) {
  3180. return TextHitInfo.leading(pos - composedTextStart.getOffset());
  3181. } else {
  3182. return null;
  3183. }
  3184. }
  3185. }
  3186. public Rectangle getTextLocation(TextHitInfo offset) {
  3187. Rectangle r;
  3188. try {
  3189. r = modelToView(getCaretPosition());
  3190. if (r != null) {
  3191. Point p = getLocationOnScreen();
  3192. r.translate(p.x, p.y);
  3193. }
  3194. } catch (BadLocationException ble) {
  3195. r = null;
  3196. }
  3197. if (r == null)
  3198. r = new Rectangle();
  3199. return r;
  3200. }
  3201. public AttributedCharacterIterator getSelectedText(
  3202. Attribute[] attributes) {
  3203. String selection = JTextComponent.this.getSelectedText();
  3204. if (selection != null) {
  3205. return new AttributedString(selection).getIterator();
  3206. } else {
  3207. return null;
  3208. }
  3209. }
  3210. // --- DocumentListener methods ---
  3211. public void changedUpdate(DocumentEvent e) {
  3212. latestCommittedTextStart = latestCommittedTextEnd = null;
  3213. }
  3214. public void insertUpdate(DocumentEvent e) {
  3215. latestCommittedTextStart = latestCommittedTextEnd = null;
  3216. }
  3217. public void removeUpdate(DocumentEvent e) {
  3218. latestCommittedTextStart = latestCommittedTextEnd = null;
  3219. }
  3220. }
  3221. //
  3222. // Replaces the current input method (composed) text according to
  3223. // the passed input method event. This method also inserts the
  3224. // committed text into the document.
  3225. //
  3226. private void replaceInputMethodText(InputMethodEvent e) {
  3227. int commitCount = e.getCommittedCharacterCount();
  3228. AttributedCharacterIterator text = e.getText();
  3229. int composedTextIndex;
  3230. // old composed text deletion
  3231. Document doc = getDocument();
  3232. if (composedTextExists()) {
  3233. try {
  3234. doc.remove(composedTextStart.getOffset(),
  3235. composedTextEnd.getOffset() -
  3236. composedTextStart.getOffset());
  3237. } catch (BadLocationException ble) {}
  3238. composedTextStart = composedTextEnd = null;
  3239. composedTextAttribute = null;
  3240. composedTextContent = null;
  3241. }
  3242. if (text != null) {
  3243. text.first();
  3244. int committedTextStartIndex = 0;
  3245. int committedTextEndIndex = 0;
  3246. // committed text insertion
  3247. if (commitCount > 0) {
  3248. // Remember latest committed text start index
  3249. committedTextStartIndex = caret.getDot();
  3250. // Need to generate KeyTyped events for the committed text for components
  3251. // that are not aware they are active input method clients.
  3252. if (shouldSynthensizeKeyEvents()) {
  3253. for (char c = text.current(); commitCount > 0;
  3254. c = text.next(), commitCount--) {
  3255. KeyEvent ke = new KeyEvent(this, KeyEvent.KEY_TYPED,
  3256. EventQueue.getMostRecentEventTime(),
  3257. 0, KeyEvent.VK_UNDEFINED, c);
  3258. processKeyEvent(ke);
  3259. }
  3260. } else {
  3261. StringBuffer strBuf = new StringBuffer();
  3262. for (char c = text.current(); commitCount > 0;
  3263. c = text.next(), commitCount--) {
  3264. strBuf.append(c);
  3265. }
  3266. // map it to an ActionEvent
  3267. mapCommittedTextToAction(new String(strBuf));
  3268. }
  3269. // Remember latest committed text end index
  3270. committedTextEndIndex = caret.getDot();
  3271. }
  3272. // new composed text insertion
  3273. composedTextIndex = text.getIndex();
  3274. if (composedTextIndex < text.getEndIndex()) {
  3275. createComposedTextAttribute(composedTextIndex, text);
  3276. try {
  3277. replaceSelection(null);
  3278. doc.insertString(caret.getDot(), composedTextContent,
  3279. composedTextAttribute);
  3280. composedTextStart = doc.createPosition(caret.getDot() -
  3281. composedTextContent.length());
  3282. composedTextEnd = doc.createPosition(caret.getDot());
  3283. } catch (BadLocationException ble) {
  3284. composedTextStart = composedTextEnd = null;
  3285. composedTextAttribute = null;
  3286. composedTextContent = null;
  3287. }
  3288. }
  3289. // Save the latest committed text information
  3290. if (committedTextStartIndex != committedTextEndIndex) {
  3291. try {
  3292. latestCommittedTextStart = doc.
  3293. createPosition(committedTextStartIndex);
  3294. latestCommittedTextEnd = doc.
  3295. createPosition(committedTextEndIndex);
  3296. } catch (BadLocationException ble) {
  3297. latestCommittedTextStart =
  3298. latestCommittedTextEnd = null;
  3299. }
  3300. } else {
  3301. latestCommittedTextStart =
  3302. latestCommittedTextEnd = null;
  3303. }
  3304. }
  3305. }
  3306. private void createComposedTextAttribute(int composedIndex,
  3307. AttributedCharacterIterator text) {
  3308. Document doc = getDocument();
  3309. StringBuffer strBuf = new StringBuffer();
  3310. // create attributed string with no attributes
  3311. for (char c = text.setIndex(composedIndex);
  3312. c != CharacterIterator.DONE; c = text.next()) {
  3313. strBuf.append(c);
  3314. }
  3315. composedTextContent = new String(strBuf);
  3316. composedTextAttribute = new SimpleAttributeSet();
  3317. composedTextAttribute.addAttribute(StyleConstants.ComposedTextAttribute,
  3318. new AttributedString(text, composedIndex, text.getEndIndex()));
  3319. }
  3320. private boolean saveComposedText(int pos) {
  3321. if (composedTextExists()) {
  3322. int start = composedTextStart.getOffset();
  3323. int len = composedTextEnd.getOffset() -
  3324. composedTextStart.getOffset();
  3325. if (pos >= start && pos <= start + len) {
  3326. try {
  3327. getDocument().remove(start, len);
  3328. return true;
  3329. } catch (BadLocationException ble) {}
  3330. }
  3331. }
  3332. return false;
  3333. }
  3334. private void restoreComposedText() {
  3335. Document doc = getDocument();
  3336. try {
  3337. doc.insertString(caret.getDot(),
  3338. composedTextContent,
  3339. composedTextAttribute);
  3340. composedTextStart = doc.createPosition(caret.getDot() -
  3341. composedTextContent.length());
  3342. composedTextEnd = doc.createPosition(caret.getDot());
  3343. } catch (BadLocationException ble) {}
  3344. }
  3345. //
  3346. // Map committed text to an ActionEvent. If the committed text length is 1,
  3347. // treat it as a KeyStroke, otherwise or there is no KeyStroke defined,
  3348. // treat it just as a default action.
  3349. //
  3350. private void mapCommittedTextToAction(String committedText) {
  3351. Keymap binding = getKeymap();
  3352. if (binding != null) {
  3353. Action a = null;
  3354. if (committedText.length() == 1) {
  3355. KeyStroke k = KeyStroke.getKeyStroke(committedText.charAt(0));
  3356. a = binding.getAction(k);
  3357. }
  3358. if (a == null) {
  3359. a = binding.getDefaultAction();
  3360. }
  3361. if (a != null) {
  3362. ActionEvent ae =
  3363. new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
  3364. committedText,
  3365. EventQueue.getMostRecentEventTime(),
  3366. getCurrentEventModifiers());
  3367. a.actionPerformed(ae);
  3368. }
  3369. }
  3370. }
  3371. //
  3372. // Sets the caret position according to the passed input method
  3373. // event. Also, sets/resets composed text caret appropriately.
  3374. //
  3375. private void setInputMethodCaretPosition(InputMethodEvent e) {
  3376. int dot;
  3377. if (composedTextExists()) {
  3378. dot = composedTextStart.getOffset();
  3379. if (!(caret instanceof ComposedTextCaret)) {
  3380. if (composedTextCaret == null) {
  3381. composedTextCaret = new ComposedTextCaret();
  3382. }
  3383. originalCaret = caret;
  3384. // Sets composed text caret
  3385. exchangeCaret(originalCaret, composedTextCaret);
  3386. }
  3387. TextHitInfo caretPos = e.getCaret();
  3388. if (caretPos != null) {
  3389. int index = caretPos.getInsertionIndex();
  3390. dot += index;
  3391. if (index == 0) {
  3392. // Scroll the component if needed so that the composed text
  3393. // becomes visible.
  3394. try {
  3395. Rectangle d = modelToView(dot);
  3396. Rectangle end = modelToView(composedTextEnd.getOffset());
  3397. Rectangle b = getBounds();
  3398. d.x += Math.min(end.x - d.x, b.width);
  3399. scrollRectToVisible(d);
  3400. } catch (BadLocationException ble) {}
  3401. }
  3402. }
  3403. caret.setDot(dot);
  3404. } else if (caret instanceof ComposedTextCaret) {
  3405. dot = caret.getDot();
  3406. // Restores original caret
  3407. exchangeCaret(caret, originalCaret);
  3408. caret.setDot(dot);
  3409. }
  3410. }
  3411. private void exchangeCaret(Caret oldCaret, Caret newCaret) {
  3412. int blinkRate = oldCaret.getBlinkRate();
  3413. setCaret(newCaret);
  3414. caret.setBlinkRate(blinkRate);
  3415. caret.setVisible(hasFocus());
  3416. }
  3417. /**
  3418. * Returns true if KeyEvents should be synthesized from an InputEvent.
  3419. */
  3420. private boolean shouldSynthensizeKeyEvents() {
  3421. if (!checkedInputOverride) {
  3422. checkedInputOverride = true;
  3423. needToSendKeyTypedEvent =
  3424. !isProcessInputMethodEventOverridden();
  3425. }
  3426. return needToSendKeyTypedEvent;
  3427. }
  3428. //
  3429. // Checks whether the client code overrides processInputMethodEvent. If it is overridden,
  3430. // need not to generate KeyTyped events for committed text. If it's not, behave as an
  3431. // passive input method client.
  3432. //
  3433. private boolean isProcessInputMethodEventOverridden() {
  3434. if (overrideMap == null) {
  3435. overrideMap = Collections.synchronizedMap(new HashMap());
  3436. }
  3437. Boolean retValue = (Boolean)overrideMap.get(getClass().getName());
  3438. if (retValue != null) {
  3439. return retValue.booleanValue();
  3440. }
  3441. Boolean ret = (Boolean)AccessController.doPrivileged(new
  3442. PrivilegedAction() {
  3443. public Object run() {
  3444. return isProcessInputMethodEventOverridden(
  3445. JTextComponent.this.getClass());
  3446. }
  3447. });
  3448. return ret.booleanValue();
  3449. }
  3450. //
  3451. // Checks whether a composed text in this text component
  3452. //
  3453. boolean composedTextExists() {
  3454. return (composedTextStart != null);
  3455. }
  3456. //
  3457. // Caret implementation for editing the composed text.
  3458. //
  3459. class ComposedTextCaret extends DefaultCaret implements Serializable {
  3460. Color bg;
  3461. //
  3462. // Get the background color of the component
  3463. //
  3464. public void install(JTextComponent c) {
  3465. super.install(c);
  3466. Document doc = c.getDocument();
  3467. if (doc instanceof StyledDocument) {
  3468. StyledDocument sDoc = (StyledDocument)doc;
  3469. Element elem = sDoc.getCharacterElement(c.composedTextStart.getOffset());
  3470. AttributeSet attr = elem.getAttributes();
  3471. bg = sDoc.getBackground(attr);
  3472. }
  3473. if (bg == null) {
  3474. bg = c.getBackground();
  3475. }
  3476. }
  3477. //
  3478. // Draw caret in XOR mode.
  3479. //
  3480. public void paint(Graphics g) {
  3481. if(isVisible()) {
  3482. try {
  3483. Rectangle r = component.modelToView(getDot());
  3484. g.setXORMode(bg);
  3485. g.drawLine(r.x, r.y, r.x, r.y + r.height - 1);
  3486. g.setPaintMode();
  3487. } catch (BadLocationException e) {
  3488. // can't render I guess
  3489. //System.err.println("Can't render cursor");
  3490. }
  3491. }
  3492. }
  3493. //
  3494. // If some area other than the composed text is clicked by mouse,
  3495. // issue endComposition() to force commit the composed text.
  3496. //
  3497. protected void positionCaret(MouseEvent me) {
  3498. JTextComponent host = component;
  3499. Point pt = new Point(me.getX(), me.getY());
  3500. int offset = host.viewToModel(pt);
  3501. int composedStartIndex = host.composedTextStart.getOffset();
  3502. if ((offset < composedStartIndex) ||
  3503. (offset > composedTextEnd.getOffset())) {
  3504. try {
  3505. // Issue endComposition
  3506. Position newPos = host.getDocument().createPosition(offset);
  3507. host.getInputContext().endComposition();
  3508. // Post a caret positioning runnable to assure that the positioning
  3509. // occurs *after* committing the composed text.
  3510. EventQueue.invokeLater(new DoSetCaretPosition(host, newPos));
  3511. } catch (BadLocationException ble) {
  3512. System.err.println(ble);
  3513. }
  3514. } else {
  3515. // Normal processing
  3516. super.positionCaret(me);
  3517. }
  3518. }
  3519. }
  3520. //
  3521. // Runnable class for invokeLater() to set caret position later.
  3522. //
  3523. private class DoSetCaretPosition implements Runnable {
  3524. JTextComponent host;
  3525. Position newPos;
  3526. DoSetCaretPosition(JTextComponent host, Position newPos) {
  3527. this.host = host;
  3528. this.newPos = newPos;
  3529. }
  3530. public void run() {
  3531. host.setCaretPosition(newPos.getOffset());
  3532. }
  3533. }
  3534. }