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