1. /*
  2. * @(#)JTextComponent.java 1.157 01/02/28
  3. *
  4. * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing.text;
  11. import java.util.Hashtable;
  12. import java.util.Enumeration;
  13. import java.util.Vector;
  14. import java.io.*;
  15. import java.awt.*;
  16. import java.awt.event.*;
  17. import java.awt.datatransfer.*;
  18. import java.text.*;
  19. import javax.swing.*;
  20. import javax.swing.event.*;
  21. import javax.swing.plaf.*;
  22. import javax.accessibility.*;
  23. import java.awt.im.InputMethodRequests;
  24. import java.awt.font.TextHitInfo;
  25. import java.lang.reflect.Method;
  26. import java.security.AccessController;
  27. import java.security.PrivilegedAction;
  28. import java.text.AttributedCharacterIterator.Attribute;
  29. import java.util.Iterator;
  30. import java.util.Map;
  31. import java.util.Map.Entry;
  32. import java.util.Set;
  33. /**
  34. * JTextComponent is the base class for swing text components. It
  35. * tries to be compatible with the java.awt.TextComponent class
  36. * where it can reasonably do so. Also provided are other services
  37. * for additional flexibility (beyond the pluggable UI and bean
  38. * support).
  39. * You can find information on how to use the functionality
  40. * this class provides in
  41. * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/generaltext.html">General Rules for Using Text Components</a>,
  42. * a section in <em>The Java Tutorial.</em>
  43. *
  44. * <p>
  45. * <dl>
  46. * <dt><b><font size=+1>Caret Changes</font></b>
  47. * <dd>
  48. * The caret is a pluggable object in swing text components.
  49. * Notification of changes to the caret position and the selection
  50. * are sent to implementations of the CaretListener interface that
  51. * have been registered with the text component. The UI will
  52. * install a default caret unless a customized caret has been
  53. * set.
  54. *
  55. * <p>
  56. * <dt><b><font size=+1>Commands</font></b>
  57. * <dd>
  58. * Text components provide a number of commands that can be used
  59. * to manipulate the component. This is essentially the way that
  60. * the component expresses its capabilities. These are expressed
  61. * in terms of the swing Action interface, using the TextAction
  62. * implementation. The set of commands supported by the text
  63. * component can be found with the
  64. * {@link #getActions} method. These actions
  65. * can be bound to key events, fired from buttons, etc.
  66. *
  67. * <p>
  68. * <dt><b><font size=+1>Text Input</font></b>
  69. * <dd>
  70. * The text components support flexible and internationalized text input, using
  71. * keymaps and the input method framework, while maintaining compatibility with
  72. * the AWT listener model.
  73. * <p>
  74. * A {@link javax.swing.text.Keymap} lets an application bind key strokes to actions.
  75. * In order to allow keymaps to be shared across multiple text components, they
  76. * can use actions that extend TextAction. TextAction can determine which
  77. * JTextComponent most recently has or had focus and therefore is the subject of
  78. * the action (In the case that the ActionEvent sent to the action doesn't contain
  79. * the target text component as its source).
  80. * <p>
  81. * The <a href="../../../../guide/imf/spec.html">input method framework</a> lets
  82. * text components interact with input methods, separate software components that
  83. * preprocess events to let users enter thousands of different characters using
  84. * keyboards with far fewer keys. JTextComponent is an <em>active client</em> of
  85. * the framework, so it implements the preferred user interface for interacting
  86. * with input methods. As a consequence, some key events do not reach the text
  87. * component because they are handled by an input method, and some text input
  88. * reaches the text component as committed text within an {@link
  89. * java.awt.event.InputMethodEvent} instead of as a key event. The complete text
  90. * input is the combination of the characters in keyTyped key events and committed
  91. * text in input method events.
  92. * <p>
  93. * The AWT listener model lets applications attach event listeners to components
  94. * in order to bind events to actions. Swing encourages the use of keymaps instead
  95. * of listeners, but maintains compatibility with listeners by giving the
  96. * listeners a chance to steal an event by consuming it.
  97. * <p>
  98. * Keyboard event and input method events are handled in the following stages,
  99. * with each stage capable of consuming the event:
  100. * <table border=1>
  101. * <tr><td>Stage<td>KeyEvent <td>InputMethodEvent
  102. * <tr><td>1. <td>input methods <td>(generated here)
  103. * <tr><td>2. <td>focus manager <td>
  104. * <tr><td>3. <td>registered key listeners<td>registered input method listeners
  105. * <tr><td>4. <td> <td>input method handling in JTextComponent
  106. * <tr><td>5. <td colspan=2>keymap handling using the current keymap
  107. * <tr><td>6. <td>keyboard handling in JComponent (e.g. accelerators, component navigation, etc.)<td>
  108. * </table>
  109. * <p>
  110. * To maintain compatibility with applications that listen to key events but are
  111. * not aware of input method events, the input method handling in stage 4 provides
  112. * a compatibility mode for components that do not process input method events.
  113. * For these components, the committed text is converted to keyTyped key events
  114. * and processed in the key event pipeline starting at stage 3 instead of in the
  115. * input method event pipeline.
  116. * <p>
  117. * By default the component will create a keymap (named <b>DEFAULT_KEYMAP</b>)
  118. * that is shared by all JTextComponent instances as the default keymap.
  119. * Typically a look-and-feel implementation will install a different keymap
  120. * that resolves to the default keymap for those bindings not found in the
  121. * different keymap. The minimal bindings include:
  122. * <ul>
  123. * <li>inserting content into the editor for the
  124. * printable keys.
  125. * <li>removing content with the backspace and del
  126. * keys.
  127. * <li>caret movement forward and backward
  128. * </ul>
  129. *
  130. * <p>
  131. * <dt><b><font size=+1>Model/View Split</font></b>
  132. * <dd>
  133. * The text components have a model-view split. A text component pulls
  134. * together the objects used to represent the model, view, and controller.
  135. * The text document model may be shared by other views which act as observers
  136. * of the model (e.g. a document may be shared by multiple components).
  137. *
  138. * <p align=center><img src="doc-files/editor.gif" HEIGHT=358 WIDTH=587></p>
  139. *
  140. * <p>
  141. * The model is defined by the {@link Document} interface.
  142. * This is intended to provide a flexible text storage mechanism
  143. * that tracks change during edits and can be extended to more sophisticated
  144. * models. The model interfaces are meant to capture the capabilities of
  145. * expression given by SGML, a system used to express a wide variety of
  146. * content.
  147. * Each modification to the document causes notification of the
  148. * details of the change to be sent to all observers in the form of a
  149. * {@link DocumentEvent} which allows the views to stay up to date with the model.
  150. * This event is sent to observers that have implemented the
  151. * {@link DocumentListener}
  152. * interface and registered interest with the model being observed.
  153. *
  154. * <p>
  155. * <dt><b><font size=+1>Location Information</font></b>
  156. * <dd>
  157. * The capability of determining the location of text in
  158. * the view is provided. There are two methods, {@link #modelToView}
  159. * and {@link #viewToModel} for determining this information.
  160. *
  161. * <p>
  162. * <dt><b><font size=+1>Undo/Redo support</font></b>
  163. * <dd>
  164. * Support for an edit history mechanism is provided to allow
  165. * undo/redo operations. The text component does not itself
  166. * provide the history buffer by default, but does provide
  167. * the UndoableEdit records that can be used in conjunction
  168. * with a history buffer to provide the undo/redo support.
  169. * The support is provided by the Document model, which allows
  170. * one to attach UndoableEditListener implementations.
  171. *
  172. * <p>
  173. * <dt><b><font size=+1>Thread Safety</font></b>
  174. * <dd>
  175. * The swing text components provide some support of thread
  176. * safe operations. Because of the high level of configurability
  177. * of the text components, it is possible to circumvent the
  178. * protection provided. The protection primarily comes from
  179. * the model, so the documentation of AbstractDocument
  180. * describes the assumptions of the protection provided.
  181. * The methods that are safe to call asynchronously are marked
  182. * with comments.
  183. * </dl>
  184. *
  185. * <p>
  186. * <strong>Warning:</strong>
  187. * Serialized objects of this class will not be compatible with
  188. * future Swing releases. The current serialization support is appropriate
  189. * for short term storage or RMI between applications running the same
  190. * version of Swing. A future release of Swing will provide support for
  191. * long term persistence.
  192. *
  193. * @beaninfo
  194. * attribute: isContainer false
  195. *
  196. * @author Timothy Prinzing
  197. * @version 1.157 02/28/01
  198. * @see Document
  199. * @see DocumentEvent
  200. * @see DocumentListener
  201. * @see Caret
  202. * @see CaretEvent
  203. * @see CaretListener
  204. * @see TextUI
  205. * @see View
  206. * @see ViewFactory
  207. /* */
  208. public abstract class JTextComponent extends JComponent implements Scrollable, Accessible
  209. {
  210. /**
  211. * Creates a new JTextComponent.
  212. * Listeners for caret events are established, and the pluggable
  213. * UI installed. The component is marked as editable. No layout manager
  214. * is used, because layout is managed by the view subsystem of text.
  215. * The document model is set to null.
  216. */
  217. public JTextComponent() {
  218. super();
  219. // Will cause the system clipboard state to be updated.
  220. canAccessSystemClipboard = true;
  221. canAccessSystemClipboard();
  222. // enable InputMethodEvent for on-the-spot pre-editing
  223. enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.INPUT_METHOD_EVENT_MASK);
  224. needToSendKeyTypedEvent = !isProcessInputMethodEventOverridden();
  225. caretEvent = new MutableCaretEvent(this);
  226. addMouseListener(caretEvent);
  227. addFocusListener(caretEvent);
  228. setEditable(true);
  229. setLayout(null); // layout is managed by View hierarchy
  230. updateUI();
  231. }
  232. /**
  233. * Fetches the user-interface factory for this text-oriented editor.
  234. *
  235. * @return the factory
  236. */
  237. public TextUI getUI() { return (TextUI)ui; }
  238. /**
  239. * Sets the user-interface factory for this text-oriented editor
  240. *
  241. * @param ui the factory
  242. */
  243. public void setUI(TextUI ui) {
  244. super.setUI(ui);
  245. }
  246. /**
  247. * Reloads the pluggable UI. The key used to fetch the
  248. * new interface is <b>getUIClassID()</b>. The type of
  249. * the UI is <b>TextUI</b>. invalidate() is called after
  250. * setting the UI.
  251. */
  252. public void updateUI() {
  253. setUI((TextUI)UIManager.getUI(this));
  254. invalidate();
  255. }
  256. /**
  257. * Adds a caret listener for notification of any changes
  258. * to the caret.
  259. *
  260. * @param listener the listener
  261. * @see javax.swing.event.CaretEvent
  262. */
  263. public void addCaretListener(CaretListener listener) {
  264. listenerList.add(CaretListener.class, listener);
  265. }
  266. /**
  267. * Removes a caret listener.
  268. *
  269. * @param listener the listener
  270. * @see javax.swing.event.CaretEvent
  271. */
  272. public void removeCaretListener(CaretListener listener) {
  273. listenerList.remove(CaretListener.class, listener);
  274. }
  275. /**
  276. * Notifies all listeners that have registered interest for
  277. * notification on this event type. The event instance
  278. * is lazily created using the parameters passed into
  279. * the fire method. The listener list is processed in a
  280. * last-to-first manner.
  281. *
  282. * @param e the event
  283. * @see EventListenerList
  284. */
  285. protected void fireCaretUpdate(CaretEvent e) {
  286. // Guaranteed to return a non-null array
  287. Object[] listeners = listenerList.getListenerList();
  288. // Process the listeners last to first, notifying
  289. // those that are interested in this event
  290. for (int i = listeners.length-2; i>=0; i-=2) {
  291. if (listeners[i]==CaretListener.class) {
  292. ((CaretListener)listeners[i+1]).caretUpdate(e);
  293. }
  294. }
  295. }
  296. /**
  297. * Associates the editor with a text document.
  298. * The currently registered factory is used to build a view for
  299. * the document, which gets displayed by the editor after revalidation.
  300. * A PropertyChange event ("document") is propagated to each listener.
  301. *
  302. * @param doc the document to display/edit
  303. * @see #getDocument
  304. * @beaninfo
  305. * description: the text document model
  306. * bound: true
  307. * expert: true
  308. */
  309. public void setDocument(Document doc) {
  310. if (accessibleContext != null) {
  311. model.removeDocumentListener(
  312. ((AccessibleJTextComponent)accessibleContext));
  313. }
  314. Document old = model;
  315. model = doc;
  316. firePropertyChange("document", old, doc);
  317. revalidate();
  318. repaint();
  319. if (accessibleContext != null) {
  320. model.addDocumentListener(
  321. ((AccessibleJTextComponent)accessibleContext));
  322. }
  323. }
  324. /**
  325. * Fetches the model associated with the editor. This is
  326. * primarily for the UI to get at the minimal amount of
  327. * state required to be a text editor. Subclasses will
  328. * return the actual type of the model which will typically
  329. * be something that extends Document.
  330. *
  331. * @return the model
  332. */
  333. public Document getDocument() {
  334. return model;
  335. }
  336. /**
  337. * Fetches the command list for the editor. This is
  338. * the list of commands supported by the plugged-in UI
  339. * augmented by the collection of commands that the
  340. * editor itself supports. These are useful for binding
  341. * to events, such as in a keymap.
  342. *
  343. * @return the command list
  344. */
  345. public Action[] getActions() {
  346. return getUI().getEditorKit(this).getActions();
  347. }
  348. /**
  349. * Sets margin space between the text component's border
  350. * and its text. The text component's default Border
  351. * object will use this value to create the proper margin.
  352. * However, if a non-default border is set on the text component,
  353. * it is that Border object's responsibility to create the
  354. * appropriate margin space (else this property will effectively
  355. * be ignored). This causes a redraw of the component.
  356. * A PropertyChange event ("margin") is sent to all listeners.
  357. *
  358. * @param m the space between the border and the text
  359. * @beaninfo
  360. * description: desired space between the border and text area
  361. * bound: true
  362. */
  363. public void setMargin(Insets m) {
  364. Insets old = margin;
  365. margin = m;
  366. firePropertyChange("margin", old, m);
  367. invalidate();
  368. }
  369. /**
  370. * Returns the margin between the text component's border and
  371. * its text.
  372. *
  373. * @return the margin
  374. */
  375. public Insets getMargin() {
  376. return margin;
  377. }
  378. /**
  379. * Fetches the caret that allows text-oriented navigation over
  380. * the view.
  381. *
  382. * @return the caret
  383. */
  384. public Caret getCaret() {
  385. return caret;
  386. }
  387. /**
  388. * Sets the caret to be used. By default this will be set
  389. * by the UI that gets installed. This can be changed to
  390. * a custom caret if desired. Setting the caret results in a
  391. * PropertyChange event ("caret") being fired.
  392. *
  393. * @param c the caret
  394. * @see #getCaret
  395. * @beaninfo
  396. * description: the caret used to select/navigate
  397. * bound: true
  398. * expert: true
  399. */
  400. public void setCaret(Caret c) {
  401. if (caret != null) {
  402. caret.removeChangeListener(caretEvent);
  403. caret.deinstall(this);
  404. }
  405. Caret old = caret;
  406. caret = c;
  407. if (caret != null) {
  408. caret.install(this);
  409. caret.addChangeListener(caretEvent);
  410. }
  411. firePropertyChange("caret", old, caret);
  412. }
  413. /**
  414. * Fetches the object responsible for making highlights.
  415. *
  416. * @return the highlighter
  417. */
  418. public Highlighter getHighlighter() {
  419. return highlighter;
  420. }
  421. /**
  422. * Sets the highlighter to be used. By default this will be set
  423. * by the UI that gets installed. This can be changed to
  424. * a custom highlighter if desired. The highlighter can be set to
  425. * null to disable it. A PropertyChange event ("highlighter") is fired
  426. * when a new highlighter is installed.
  427. *
  428. * @param h the highlighter
  429. * @see #getHighlighter
  430. * @beaninfo
  431. * description: object responsible for background highlights
  432. * bound: true
  433. * expert: true
  434. */
  435. public void setHighlighter(Highlighter h) {
  436. if (highlighter != null) {
  437. highlighter.deinstall(this);
  438. }
  439. Highlighter old = highlighter;
  440. highlighter = h;
  441. if (highlighter != null) {
  442. highlighter.install(this);
  443. }
  444. firePropertyChange("highlighter", old, h);
  445. }
  446. /**
  447. * Sets the keymap to use for binding events to
  448. * actions. Setting to null effectively disables keyboard input.
  449. * A PropertyChange event ("keymap") is fired when a new keymap
  450. * is installed.
  451. *
  452. * @param map the keymap
  453. * @see #getKeymap
  454. * @beaninfo
  455. * description: set of key event to action bindings to use
  456. * bound: true
  457. */
  458. public void setKeymap(Keymap map) {
  459. Keymap old = keymap;
  460. keymap = map;
  461. firePropertyChange("keymap", old, keymap);
  462. updateInputMap(old, map);
  463. }
  464. /**
  465. * Updates the InputMaps in response to a Keymap change.
  466. */
  467. void updateInputMap(Keymap oldKm, Keymap newKm) {
  468. // Locate the current KeymapWrapper.
  469. InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
  470. InputMap last = km;
  471. while (km != null && !(km instanceof KeymapWrapper)) {
  472. last = km;
  473. km = km.getParent();
  474. }
  475. if (km != null) {
  476. // Found it, tweak the InputMap that points to it, as well
  477. // as anything it points to.
  478. if (newKm == null) {
  479. if (last != km) {
  480. last.setParent(km.getParent());
  481. }
  482. else {
  483. last.setParent(null);
  484. }
  485. }
  486. else {
  487. InputMap newKM = new KeymapWrapper(newKm);
  488. last.setParent(newKM);
  489. if (last != km) {
  490. newKM.setParent(km.getParent());
  491. }
  492. }
  493. }
  494. else if (newKm != null) {
  495. km = getInputMap(JComponent.WHEN_FOCUSED);
  496. if (km != null) {
  497. // Couldn't find it.
  498. // Set the parent of WHEN_FOCUSED InputMap to be the new one.
  499. InputMap newKM = new KeymapWrapper(newKm);
  500. newKM.setParent(km.getParent());
  501. km.setParent(newKM);
  502. }
  503. }
  504. // Do the same thing with the ActionMap
  505. ActionMap am = getActionMap();
  506. ActionMap lastAM = am;
  507. while (am != null && !(am instanceof KeymapActionMap)) {
  508. lastAM = am;
  509. am = am.getParent();
  510. }
  511. if (am != null) {
  512. // Found it, tweak the Actionap that points to it, as well
  513. // as anything it points to.
  514. if (newKm == null) {
  515. if (lastAM != am) {
  516. lastAM.setParent(am.getParent());
  517. }
  518. else {
  519. lastAM.setParent(null);
  520. }
  521. }
  522. else {
  523. ActionMap newAM = new KeymapActionMap(newKm);
  524. lastAM.setParent(newAM);
  525. if (lastAM != am) {
  526. newAM.setParent(am.getParent());
  527. }
  528. }
  529. }
  530. else if (newKm != null) {
  531. am = getActionMap();
  532. if (am != null) {
  533. // Couldn't find it.
  534. // Set the parent of ActionMap to be the new one.
  535. ActionMap newAM = new KeymapActionMap(newKm);
  536. newAM.setParent(am.getParent());
  537. am.setParent(newAM);
  538. }
  539. }
  540. }
  541. /**
  542. * Fetches the keymap currently active in this text
  543. * component.
  544. *
  545. * @return the keymap
  546. */
  547. public Keymap getKeymap() {
  548. return keymap;
  549. }
  550. /**
  551. * Adds a new keymap into the keymap hierarchy. Keymap bindings
  552. * resolve from bottom up so an attribute specified in a child
  553. * will override an attribute specified in the parent.
  554. *
  555. * @param nm the name of the keymap (must be unique within the
  556. * collection of named keymaps in the document). The name may
  557. * be null if the keymap is unnamed, but the caller is responsible
  558. * for managing the reference returned as an unnamed keymap can't
  559. * be fetched by name.
  560. * @param parent the parent keymap. This may be null if unspecified
  561. * bindings need not be resolved in some other keymap.
  562. * @return the keymap
  563. */
  564. public static Keymap addKeymap(String nm, Keymap parent) {
  565. Keymap map = new DefaultKeymap(nm, parent);
  566. if (nm != null) {
  567. // add a named keymap, a class of bindings
  568. keymapTable.put(nm, map);
  569. }
  570. return map;
  571. }
  572. /**
  573. * Removes a named keymap previously added to the document. Keymaps
  574. * with null names may not be removed in this way.
  575. *
  576. * @param nm the name of the keymap to remove
  577. * @return the keymap that was removed
  578. */
  579. public static Keymap removeKeymap(String nm) {
  580. return (Keymap) keymapTable.remove(nm);
  581. }
  582. /**
  583. * Fetches a named keymap previously added to the document.
  584. * This does not work with null-named keymaps.
  585. *
  586. * @param nm the name of the keymap
  587. * @return the keymap
  588. */
  589. public static Keymap getKeymap(String nm) {
  590. return (Keymap) keymapTable.get(nm);
  591. }
  592. /**
  593. * Binding record for creating key bindings.
  594. * <p>
  595. * <strong>Warning:</strong>
  596. * Serialized objects of this class will not be compatible with
  597. * future Swing releases. The current serialization support is appropriate
  598. * for short term storage or RMI between applications running the same
  599. * version of Swing. A future release of Swing will provide support for
  600. * long term persistence.
  601. */
  602. public static class KeyBinding {
  603. /**
  604. * The key.
  605. */
  606. public KeyStroke key;
  607. /**
  608. * The name of the action for the key.
  609. */
  610. public String actionName;
  611. /**
  612. * Creates a new key binding.
  613. *
  614. * @param key the key
  615. * @param actionName the name of the action for the key
  616. */
  617. public KeyBinding(KeyStroke key, String actionName) {
  618. this.key = key;
  619. this.actionName = actionName;
  620. }
  621. }
  622. /**
  623. * <p>
  624. * Loads a keymap with a bunch of
  625. * bindings. This can be used to take a static table of
  626. * definitions and load them into some keymap. The following
  627. * example illustrates an example of binding some keys to
  628. * the cut, copy, and paste actions associated with a
  629. * JTextComponent. A code fragment to accomplish
  630. * this might look as follows:
  631. * <pre><code>
  632. *
  633. * static final JTextComponent.KeyBinding[] defaultBindings = {
  634. * new JTextComponent.KeyBinding(
  635. * KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
  636. * DefaultEditorKit.copyAction),
  637. * new JTextComponent.KeyBinding(
  638. * KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
  639. * DefaultEditorKit.pasteAction),
  640. * new JTextComponent.KeyBinding(
  641. * KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
  642. * DefaultEditorKit.cutAction),
  643. * };
  644. *
  645. * JTextComponent c = new JTextPane();
  646. * Keymap k = c.getKeymap();
  647. * JTextComponent.loadKeymap(k, defaultBindings, c.getActions());
  648. *
  649. * </code></pre>
  650. * The sets of bindings and actions may be empty but must be non-null.
  651. *
  652. * @param map the keymap
  653. * @param bindings the bindings
  654. * @param actions the set of actions
  655. */
  656. public static void loadKeymap(Keymap map, KeyBinding[] bindings, Action[] actions) {
  657. Hashtable h = new Hashtable();
  658. for (int i = 0; i < actions.length; i++) {
  659. Action a = actions[i];
  660. String value = (String)a.getValue(Action.NAME);
  661. h.put((value!=null ? value:""), a);
  662. }
  663. for (int i = 0; i < bindings.length; i++) {
  664. Action a = (Action) h.get(bindings[i].actionName);
  665. if (a != null) {
  666. map.addActionForKeyStroke(bindings[i].key, a);
  667. }
  668. }
  669. }
  670. /**
  671. * Fetches the current color used to render the
  672. * caret.
  673. *
  674. * @return the color
  675. */
  676. public Color getCaretColor() {
  677. return caretColor;
  678. }
  679. /**
  680. * Sets the current color used to render the
  681. * caret. Setting to null effectively restores the default color.
  682. * Setting the color results in a PropertyChange event ("caretColor")
  683. * being fired.
  684. *
  685. * @param c the color
  686. * @see #getCaretColor
  687. * @beaninfo
  688. * description: the color used to render the caret
  689. * bound: true
  690. * preferred: true
  691. */
  692. public void setCaretColor(Color c) {
  693. Color old = caretColor;
  694. caretColor = c;
  695. firePropertyChange("caretColor", old, caretColor);
  696. }
  697. /**
  698. * Fetches the current color used to render the
  699. * selection.
  700. *
  701. * @return the color
  702. */
  703. public Color getSelectionColor() {
  704. return selectionColor;
  705. }
  706. /**
  707. * Sets the current color used to render the
  708. * selection. Setting the color to null is the same as setting
  709. * Color.white. Setting the color results in a PropertyChange event
  710. * ("selectionColor").
  711. *
  712. * @param c the color
  713. * @see #getSelectionColor
  714. * @beaninfo
  715. * description: color used to render selection background
  716. * bound: true
  717. * preferred: true
  718. */
  719. public void setSelectionColor(Color c) {
  720. Color old = selectionColor;
  721. selectionColor = c;
  722. firePropertyChange("selectionColor", old, selectionColor);
  723. }
  724. /**
  725. * Fetches the current color used to render the
  726. * selected text.
  727. *
  728. * @return the color
  729. */
  730. public Color getSelectedTextColor() {
  731. return selectedTextColor;
  732. }
  733. /**
  734. * Sets the current color used to render the
  735. * selected text. Setting the color to null is the same as Color.black.
  736. * Setting the color results in a PropertyChange event
  737. * ("selectedTextColor") being fired.
  738. *
  739. * @param c the color
  740. * @see #getSelectedTextColor
  741. * @beaninfo
  742. * description: color used to render selected text
  743. * bound: true
  744. * preferred: true
  745. */
  746. public void setSelectedTextColor(Color c) {
  747. Color old = selectedTextColor;
  748. selectedTextColor = c;
  749. firePropertyChange("selectedTextColor", old, selectedTextColor);
  750. }
  751. /**
  752. * Fetches the current color used to render the
  753. * selected text.
  754. *
  755. * @return the color
  756. */
  757. public Color getDisabledTextColor() {
  758. return disabledTextColor;
  759. }
  760. /**
  761. * Sets the current color used to render the
  762. * disabled text. Setting the color fires off a
  763. * PropertyChange event ("disabledTextColor").
  764. *
  765. * @param c the color
  766. * @see #getDisabledTextColor
  767. * @beaninfo
  768. * description: color used to render disabled text
  769. * bound: true
  770. * preferred: true
  771. */
  772. public void setDisabledTextColor(Color c) {
  773. Color old = disabledTextColor;
  774. disabledTextColor = c;
  775. firePropertyChange("disabledTextColor", old, disabledTextColor);
  776. }
  777. /**
  778. * Replaces the currently selected content with new content
  779. * represented by the given string. If there is no selection
  780. * this amounts to an insert of the given text. If there
  781. * is no replacement text this amounts to a removal of the
  782. * current selection.
  783. * <p>
  784. * This is the method that is used by the default implementation
  785. * of the action for inserting content that gets bound to the
  786. * keymap actions.
  787. * <p>
  788. * This method is thread safe, although most Swing methods
  789. * are not. Please see
  790. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  791. * and Swing</A> for more information.
  792. *
  793. * @param content the content to replace the selection with
  794. */
  795. public void replaceSelection(String content) {
  796. Document doc = getDocument();
  797. if (doc != null) {
  798. try {
  799. int p0 = Math.min(caret.getDot(), caret.getMark());
  800. int p1 = Math.max(caret.getDot(), caret.getMark());
  801. if (p0 != p1) {
  802. doc.remove(p0, p1 - p0);
  803. }
  804. if (content != null && content.length() > 0) {
  805. doc.insertString(p0, content, null);
  806. }
  807. } catch (BadLocationException e) {
  808. getToolkit().beep();
  809. }
  810. }
  811. }
  812. /**
  813. * Fetches a portion of the text represented by the
  814. * component. Returns an empty string if length is 0.
  815. *
  816. * @param offs the offset >= 0
  817. * @param len the length >= 0
  818. * @return the text
  819. * @exception BadLocationException if the offset or length are invalid
  820. */
  821. public String getText(int offs, int len) throws BadLocationException {
  822. return getDocument().getText(offs, len);
  823. }
  824. /**
  825. * Converts the given location in the model to a place in
  826. * the view coordinate system.
  827. * The component must have a positive size for
  828. * this translation to be computed (i.e. layout cannot
  829. * be computed until the component has been sized). The
  830. * component does not have to be visible or painted.
  831. *
  832. * @param pos the position >= 0
  833. * @return the coordinates as a rectangle, with (r.x, r.y) as the location
  834. * in the coordinate system, or null if the component does
  835. * not yet have a positive size.
  836. * @exception BadLocationException if the given position does not
  837. * represent a valid location in the associated document
  838. * @see TextUI#modelToView
  839. */
  840. public Rectangle modelToView(int pos) throws BadLocationException {
  841. return getUI().modelToView(this, pos);
  842. }
  843. /**
  844. * Converts the given place in the view coordinate system
  845. * to the nearest representative location in the model.
  846. * The component must have a positive size for
  847. * this translation to be computed (i.e. layout cannot
  848. * be computed until the component has been sized). The
  849. * component does not have to be visible or painted.
  850. *
  851. * @param pt the location in the view to translate
  852. * @return the offset >= 0 from the start of the document,
  853. * or -1 if the component does not yet have a positive
  854. * size.
  855. * @see TextUI#viewToModel
  856. */
  857. public int viewToModel(Point pt) {
  858. return getUI().viewToModel(this, pt);
  859. }
  860. /**
  861. * Transfers the currently selected range in the associated
  862. * text model to the system clipboard, removing the contents
  863. * from the model. The current selection is reset. Does nothing
  864. * for null selections.
  865. */
  866. public void cut() {
  867. Clipboard clipboard;
  868. if (isEditable() && isEnabled() &&
  869. (clipboard = getClipboard()) != null) {
  870. try {
  871. int p0 = Math.min(caret.getDot(), caret.getMark());
  872. int p1 = Math.max(caret.getDot(), caret.getMark());
  873. if (p0 != p1) {
  874. Document doc = getDocument();
  875. String srcData = doc.getText(p0, p1 - p0);
  876. StringSelection contents = new StringSelection(srcData);
  877. clipboard.setContents(contents, defaultClipboardOwner);
  878. doc.remove(p0, p1 - p0);
  879. }
  880. } catch (BadLocationException e) {
  881. }
  882. } else {
  883. getToolkit().beep();
  884. }
  885. }
  886. /**
  887. * Transfers the currently selected range in the associated
  888. * text model to the system clipboard, leaving the contents
  889. * in the text model. The current selection is remains intact.
  890. * Does nothing for null selections.
  891. */
  892. public void copy() {
  893. Clipboard clipboard;
  894. if ((clipboard = getClipboard()) != null) {
  895. try {
  896. int p0 = Math.min(caret.getDot(), caret.getMark());
  897. int p1 = Math.max(caret.getDot(), caret.getMark());
  898. if (p0 != p1) {
  899. Document doc = getDocument();
  900. String srcData = doc.getText(p0, p1 - p0);
  901. StringSelection contents = new StringSelection(srcData);
  902. clipboard.setContents(contents, defaultClipboardOwner);
  903. }
  904. } catch (BadLocationException e) {
  905. }
  906. }
  907. }
  908. /**
  909. * Transfers the contents of the system clipboard into the
  910. * associated text model. If there is a selection in the
  911. * associated view, it is replaced with the contents of the
  912. * clipboard. If there is no selection, the clipboard contents
  913. * are inserted in front of the current insert position in
  914. * the associated view. If the clipboard is empty, does nothing.
  915. * @see #replaceSelection
  916. */
  917. public void paste() {
  918. Clipboard clipboard;
  919. if (isEditable() && isEnabled() &&
  920. (clipboard = getClipboard()) != null) {
  921. Transferable content = clipboard.getContents(this);
  922. if (content != null) {
  923. try {
  924. String dstData = (String)(content.getTransferData(DataFlavor.stringFlavor));
  925. replaceSelection(dstData);
  926. } catch (Exception e) {
  927. getToolkit().beep();
  928. }
  929. } else {
  930. getToolkit().beep();
  931. }
  932. }
  933. }
  934. /**
  935. * Moves the caret to a new position, leaving behind a
  936. * mark defined by the last time setCaretPosition was
  937. * called. This forms a selection.
  938. *
  939. * @param pos the position
  940. * @see #setCaretPosition
  941. */
  942. public void moveCaretPosition(int pos) {
  943. caret.moveDot(pos);
  944. }
  945. /**
  946. * The bound property name for the focus accelerator.
  947. */
  948. public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
  949. /**
  950. * Sets the key accelerator that will cause the receiving text
  951. * component to get the focus. The accelerator will be the
  952. * key combination of the <em>alt</em> key and the character
  953. * given (converted to upper case). By default, there is no focus
  954. * accelerator key. Any previous key accelerator setting will be
  955. * superseded. A '\0' key setting will be registered, and has the
  956. * effect of turning off the focus accelerator. When the new key
  957. * is set, a PropertyChange event (FOCUS_ACCELERATOR_KEY) will be fired.
  958. *
  959. * @param aKey the key
  960. * @see #getFocusAccelerator
  961. * @beaninfo
  962. * description: accelerator character used to grab focus
  963. * bound: true
  964. */
  965. public void setFocusAccelerator(char aKey) {
  966. aKey = Character.toUpperCase(aKey);
  967. char old = focusAccelerator;
  968. focusAccelerator = aKey;
  969. firePropertyChange(FOCUS_ACCELERATOR_KEY, old, focusAccelerator);
  970. }
  971. /**
  972. * Returns the key accelerator that will cause the receiving
  973. * text component to get the focus. Return '\0' if no focus
  974. * accelerator has been set.
  975. *
  976. * @return the key
  977. */
  978. public char getFocusAccelerator() {
  979. return focusAccelerator;
  980. }
  981. /**
  982. * Initializes from a stream. This creates a
  983. * model of the type appropriate for the component
  984. * and initializes the model from the stream.
  985. * By default this will load the model as plain
  986. * text. Previous contents of the model are discarded.
  987. *
  988. * @param in The stream to read from
  989. * @param desc An object describing the stream. This
  990. * might be a string, a File, a URL, etc. Some kinds
  991. * of documents (such as html for example) might be
  992. * able to make use of this information. If non-null, it is
  993. * added as a property of the document.
  994. * @exception IOException as thrown by the stream being
  995. * used to initialize.
  996. * @see EditorKit#createDefaultDocument
  997. * @see #setDocument
  998. * @see PlainDocument
  999. */
  1000. public void read(Reader in, Object desc) throws IOException {
  1001. EditorKit kit = getUI().getEditorKit(this);
  1002. Document doc = kit.createDefaultDocument();
  1003. if (desc != null) {
  1004. doc.putProperty(Document.StreamDescriptionProperty, desc);
  1005. }
  1006. try {
  1007. kit.read(in, doc, 0);
  1008. setDocument(doc);
  1009. } catch (BadLocationException e) {
  1010. throw new IOException(e.getMessage());
  1011. }
  1012. }
  1013. /**
  1014. * Stores the contents of the model into the given
  1015. * stream. By default this will store the model as plain
  1016. * text.
  1017. *
  1018. * @param out the output stream
  1019. * @exception IOException on any I/O error
  1020. */
  1021. public void write(Writer out) throws IOException {
  1022. Document doc = getDocument();
  1023. try {
  1024. getUI().getEditorKit(this).write(out, doc, 0, doc.getLength());
  1025. } catch (BadLocationException e) {
  1026. throw new IOException(e.getMessage());
  1027. }
  1028. }
  1029. // --- java.awt.Component methods ----------------------------
  1030. /**
  1031. * Notifies this component that it has been removed from its
  1032. * container. This is used to remove the reference to this
  1033. * component as the last focused component, if such a reference
  1034. * exists (i.e. to support TextAction).
  1035. */
  1036. public void removeNotify() {
  1037. super.removeNotify();
  1038. if (focusedComponent == this) {
  1039. focusedComponent = null;
  1040. }
  1041. }
  1042. /**
  1043. * Returns true if the focus can be traversed. This would be false
  1044. * for components like a disabled button.
  1045. *
  1046. * @return true if the focus is traversable
  1047. */
  1048. public boolean isFocusTraversable() {
  1049. return isEnabled();
  1050. }
  1051. // --- java.awt.TextComponent methods ------------------------
  1052. /**
  1053. * Sets the position of the text insertion caret for the TextComponent.
  1054. * Note that the caret tracks change, so this may move if the underlying
  1055. * text of the component is changed. If the document is null, does
  1056. * nothing.
  1057. *
  1058. * @param position the position
  1059. * @beaninfo
  1060. * description: the caret position
  1061. */
  1062. public void setCaretPosition(int position) {
  1063. Document doc = getDocument();
  1064. if (doc != null) {
  1065. if (position > doc.getLength() || position < 0) {
  1066. throw new IllegalArgumentException("bad position: " + position);
  1067. }
  1068. caret.setDot(position);
  1069. }
  1070. }
  1071. /**
  1072. * Returns the position of the text insertion caret for the
  1073. * text component.
  1074. *
  1075. * @return the position of the text insertion caret for the
  1076. * text component >= 0
  1077. */
  1078. public int getCaretPosition() {
  1079. return caret.getDot();
  1080. }
  1081. /**
  1082. * Sets the text of this TextComponent to the specified text. If the
  1083. * text is null or empty, has the effect of simply deleting the old text.
  1084. * <p>
  1085. * This method is thread safe, although most Swing methods
  1086. * are not. Please see
  1087. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  1088. * and Swing</A> for more information.
  1089. *
  1090. * @param t the new text to be set
  1091. * @see #getText
  1092. * @beaninfo
  1093. * description: the text of this component
  1094. */
  1095. public void setText(String t) {
  1096. try {
  1097. Document doc = getDocument();
  1098. doc.remove(0, doc.getLength());
  1099. doc.insertString(0, t, null);
  1100. } catch (BadLocationException e) {
  1101. getToolkit().beep();
  1102. }
  1103. }
  1104. /**
  1105. * Returns the text contained in this TextComponent. If the underlying
  1106. * document is null, will give a NullPointerException.
  1107. *
  1108. * @return the text
  1109. * @see #setText
  1110. */
  1111. public String getText() {
  1112. Document doc = getDocument();
  1113. String txt;
  1114. try {
  1115. txt = doc.getText(0, doc.getLength());
  1116. } catch (BadLocationException e) {
  1117. txt = null;
  1118. }
  1119. return txt;
  1120. }
  1121. /**
  1122. * Returns the selected text contained in this TextComponent. If
  1123. * the selection is null or the document empty, returns null.
  1124. *
  1125. * @return the text
  1126. * @exception IllegalArgumentException if the selection doesn't
  1127. * have a valid mapping into the document for some reason
  1128. * @see #setText
  1129. */
  1130. public String getSelectedText() {
  1131. String txt = null;
  1132. int p0 = Math.min(caret.getDot(), caret.getMark());
  1133. int p1 = Math.max(caret.getDot(), caret.getMark());
  1134. if (p0 != p1) {
  1135. try {
  1136. Document doc = getDocument();
  1137. txt = doc.getText(p0, p1 - p0);
  1138. } catch (BadLocationException e) {
  1139. throw new IllegalArgumentException(e.getMessage());
  1140. }
  1141. }
  1142. return txt;
  1143. }
  1144. /**
  1145. * Returns the boolean indicating whether this TextComponent is
  1146. * editable or not.
  1147. *
  1148. * @return the boolean value
  1149. * @see #setEditable
  1150. */
  1151. public boolean isEditable() {
  1152. return editable;
  1153. }
  1154. /**
  1155. * Sets the specified boolean to indicate whether or not this
  1156. * TextComponent should be editable. A PropertyChange event ("editable")
  1157. * is fired when the state is changed.
  1158. *
  1159. * @param b the boolean to be set
  1160. * @see #isEditable
  1161. * @beaninfo
  1162. * description: specifies if the text can be edited
  1163. * bound: true
  1164. */
  1165. public void setEditable(boolean b) {
  1166. if (b != editable) {
  1167. boolean oldVal = editable;
  1168. editable = b;
  1169. if (editable) {
  1170. setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
  1171. } else {
  1172. setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
  1173. }
  1174. firePropertyChange("editable", new Boolean(oldVal), new Boolean(editable));
  1175. repaint();
  1176. }
  1177. }
  1178. /**
  1179. * Returns the selected text's start position. Return 0 for an
  1180. * empty document, or the value of dot if no selection.
  1181. *
  1182. * @return the start position >= 0
  1183. */
  1184. public int getSelectionStart() {
  1185. int start = Math.min(caret.getDot(), caret.getMark());
  1186. return start;
  1187. }
  1188. /**
  1189. * Sets the selection start to the specified position. The new
  1190. * starting point is constrained to be before or at the current
  1191. * selection end.
  1192. * <p>
  1193. * This is available for backward compatiblitity to code
  1194. * that called this method on java.awt.TextComponent. This is
  1195. * implemented to forward to the Caret implementation which
  1196. * is where the actual selection is maintained.
  1197. *
  1198. * @param selectionStart the start position of the text >= 0
  1199. * @beaninfo
  1200. * description: starting location of the selection.
  1201. */
  1202. public void setSelectionStart(int selectionStart) {
  1203. /* Route through select method to enforce consistent policy
  1204. * between selectionStart and selectionEnd.
  1205. */
  1206. select(selectionStart, getSelectionEnd());
  1207. }
  1208. /**
  1209. * Returns the selected text's end position. Return 0 if the document
  1210. * is empty, or the value of dot if there is no selection.
  1211. *
  1212. * @return the end position >= 0
  1213. */
  1214. public int getSelectionEnd() {
  1215. int end = Math.max(caret.getDot(), caret.getMark());
  1216. return end;
  1217. }
  1218. /**
  1219. * Sets the selection end to the specified position. The new
  1220. * end point is constrained to be at or after the current
  1221. * selection start.
  1222. * <p>
  1223. * This is available for backward compatiblitity to code
  1224. * that called this method on java.awt.TextComponent. This is
  1225. * implemented to forward to the Caret implementation which
  1226. * is where the actual selection is maintained.
  1227. *
  1228. * @param selectionEnd the end position of the text >= 0
  1229. * @beaninfo
  1230. * description: ending location of the selection.
  1231. */
  1232. public void setSelectionEnd(int selectionEnd) {
  1233. /* Route through select method to enforce consistent policy
  1234. * between selectionStart and selectionEnd.
  1235. */
  1236. select(getSelectionStart(), selectionEnd);
  1237. }
  1238. /**
  1239. * Selects the text found between the specified start and end
  1240. * locations. This call is provided for backward compatibility.
  1241. * It is routed to a call to setCaretPosition
  1242. * followed by a call to moveCaretPostion. The preferred way
  1243. * to manage selection is by calling those methods directly.
  1244. *
  1245. * @param selectionStart the start position of the text >= 0
  1246. * @param selectionEnd the end position of the text >= 0
  1247. * @see #setCaretPosition
  1248. * @see #moveCaretPosition
  1249. */
  1250. public void select(int selectionStart, int selectionEnd) {
  1251. // argument adjustment done by java.awt.TextComponent
  1252. if (selectionStart < 0) {
  1253. selectionStart = 0;
  1254. }
  1255. if (selectionEnd > getDocument().getLength()) {
  1256. selectionEnd = getDocument().getLength();
  1257. }
  1258. if (selectionEnd < selectionStart) {
  1259. selectionEnd = selectionStart;
  1260. }
  1261. if (selectionStart > selectionEnd) {
  1262. selectionStart = selectionEnd;
  1263. }
  1264. setCaretPosition(selectionStart);
  1265. moveCaretPosition(selectionEnd);
  1266. }
  1267. /**
  1268. * Selects all the text in the TextComponent. Does nothing on a null
  1269. * or empty document.
  1270. */
  1271. public void selectAll() {
  1272. Document doc = getDocument();
  1273. if (doc != null) {
  1274. setCaretPosition(0);
  1275. moveCaretPosition(doc.getLength());
  1276. }
  1277. }
  1278. // --- Scrollable methods ---------------------------------------------
  1279. /**
  1280. * Returns the preferred size of the viewport for a view component.
  1281. * This is implemented to do the default behavior of returning
  1282. * the preferred size of the component.
  1283. *
  1284. * @return The preferredSize of a JViewport whose view is this Scrollable.
  1285. */
  1286. public Dimension getPreferredScrollableViewportSize() {
  1287. return getPreferredSize();
  1288. }
  1289. /**
  1290. * Components that display logical rows or columns should compute
  1291. * the scroll increment that will completely expose one new row
  1292. * or column, depending on the value of orientation. Ideally,
  1293. * components should handle a partially exposed row or column by
  1294. * returning the distance required to completely expose the item.
  1295. * <p>
  1296. * The default implementation of this is to simply return 10% of
  1297. * the visible area. Subclasses are likely to be able to provide
  1298. * a much more reasonable value.
  1299. *
  1300. * @param visibleRect The view area visible within the viewport
  1301. * @param orientation Either SwingConstants.VERTICAL or
  1302. * SwingConstants.HORIZONTAL.
  1303. * @param direction Less than zero to scroll up/left, greater than
  1304. * zero for down/right.
  1305. * @return The "unit" increment for scrolling in the specified direction
  1306. * @exception IllegalArgumentException for an invalid orientation
  1307. * @see JScrollBar#setUnitIncrement
  1308. */
  1309. public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
  1310. switch(orientation) {
  1311. case SwingConstants.VERTICAL:
  1312. return visibleRect.height / 10;
  1313. case SwingConstants.HORIZONTAL:
  1314. return visibleRect.width / 10;
  1315. default:
  1316. throw new IllegalArgumentException("Invalid orientation: " + orientation);
  1317. }
  1318. }
  1319. /**
  1320. * Components that display logical rows or columns should compute
  1321. * the scroll increment that will completely expose one block
  1322. * of rows or columns, depending on the value of orientation.
  1323. * <p>
  1324. * The default implementation of this is to simply return the visible
  1325. * area. Subclasses will likely be able to provide a much more
  1326. * reasonable value.
  1327. *
  1328. * @param visibleRect The view area visible within the viewport
  1329. * @param orientation Either SwingConstants.VERTICAL or
  1330. * SwingConstants.HORIZONTAL.
  1331. * @param direction Less than zero to scroll up/left, greater than zero
  1332. * for down/right.
  1333. * @return The "block" increment for scrolling in the specified direction.
  1334. * @exception IllegalArgumentException for an invalid orientation
  1335. * @see JScrollBar#setBlockIncrement
  1336. */
  1337. public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
  1338. switch(orientation) {
  1339. case SwingConstants.VERTICAL:
  1340. return visibleRect.height;
  1341. case SwingConstants.HORIZONTAL:
  1342. return visibleRect.width;
  1343. default:
  1344. throw new IllegalArgumentException("Invalid orientation: " + orientation);
  1345. }
  1346. }
  1347. /**
  1348. * Return true if a viewport should always force the width of this
  1349. * Scrollable to match the width of the viewport. For example a normal
  1350. * text view that supported line wrapping would return true here, since it
  1351. * would be undesirable for wrapped lines to disappear beyond the right
  1352. * edge of the viewport. Note that returning true for a Scrollable
  1353. * whose ancestor is a JScrollPane effectively disables horizontal
  1354. * scrolling.
  1355. * <p>
  1356. * Scrolling containers, like JViewport, will use this method each
  1357. * time they are validated.
  1358. *
  1359. * @return true if a viewport should force the Scrollables
  1360. * width to match its own.
  1361. */
  1362. public boolean getScrollableTracksViewportWidth() {
  1363. if (getParent() instanceof JViewport) {
  1364. return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
  1365. }
  1366. return false;
  1367. }
  1368. /**
  1369. * Return true if a viewport should always force the height of this
  1370. * Scrollable to match the height of the viewport. For example a
  1371. * columnar text view that flowed text in left to right columns
  1372. * could effectively disable vertical scrolling by returning
  1373. * true here.
  1374. * <p>
  1375. * Scrolling containers, like JViewport, will use this method each
  1376. * time they are validated.
  1377. *
  1378. * @return true if a viewport should force the Scrollables height
  1379. * to match its own.
  1380. */
  1381. public boolean getScrollableTracksViewportHeight() {
  1382. if (getParent() instanceof JViewport) {
  1383. return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
  1384. }
  1385. return false;
  1386. }
  1387. /**
  1388. * Returns the clipboard to use for cut/copy/paste.
  1389. */
  1390. private Clipboard getClipboard() {
  1391. if (canAccessSystemClipboard()) {
  1392. return getToolkit().getSystemClipboard();
  1393. }
  1394. Clipboard clipboard = (Clipboard)sun.awt.AppContext.getAppContext().
  1395. get(SandboxClipboardKey);
  1396. if (clipboard == null) {
  1397. clipboard = new Clipboard("Sandboxed Text Component Clipboard");
  1398. sun.awt.AppContext.getAppContext().put(SandboxClipboardKey,
  1399. clipboard);
  1400. }
  1401. return clipboard;
  1402. }
  1403. /**
  1404. * Returns true if it is safe to access the system Clipboard.
  1405. */
  1406. private boolean canAccessSystemClipboard() {
  1407. if (canAccessSystemClipboard) {
  1408. SecurityManager sm = System.getSecurityManager();
  1409. if (sm != null) {
  1410. try {
  1411. sm.checkSystemClipboardAccess();
  1412. return true;
  1413. } catch (SecurityException se) {
  1414. canAccessSystemClipboard = false;
  1415. return false;
  1416. }
  1417. }
  1418. return true;
  1419. }
  1420. return false;
  1421. }
  1422. /////////////////
  1423. // Accessibility support
  1424. ////////////////
  1425. /**
  1426. * Gets the AccessibleContext associated with this JTextComponent.
  1427. * For text components, the AccessibleContext takes the form of an
  1428. * AccessibleJTextComponent.
  1429. * A new AccessibleJTextComponent instance is created if necessary.
  1430. *
  1431. * @return an AccessibleJTextComponent that serves as the
  1432. * AccessibleContext of this JTextComponent
  1433. */
  1434. public AccessibleContext getAccessibleContext() {
  1435. if (accessibleContext == null) {
  1436. accessibleContext = new AccessibleJTextComponent();
  1437. }
  1438. return accessibleContext;
  1439. }
  1440. /**
  1441. * This class implements accessibility support for the
  1442. * <code>JTextComponent</code> class. It provides an implementation of
  1443. * the Java Accessibility API appropriate to menu user-interface elements.
  1444. * <p>
  1445. * <strong>Warning:</strong>
  1446. * Serialized objects of this class will not be compatible with
  1447. * future Swing releases. The current serialization support is appropriate
  1448. * for short term storage or RMI between applications running the same
  1449. * version of Swing. A future release of Swing will provide support for
  1450. * long term persistence.
  1451. */
  1452. public class AccessibleJTextComponent extends AccessibleJComponent
  1453. implements AccessibleText, CaretListener, DocumentListener {
  1454. int caretPos;
  1455. /**
  1456. * Constructs an AccessibleJTextComponent. Adds a listener to track
  1457. * caret change.
  1458. */
  1459. public AccessibleJTextComponent() {
  1460. Document doc = JTextComponent.this.getDocument();
  1461. if (doc != null) {
  1462. doc.addDocumentListener(this);
  1463. }
  1464. JTextComponent.this.addCaretListener(this);
  1465. caretPos = getCaretPosition();
  1466. }
  1467. /**
  1468. * Handles caret updates (fire appropriate property change event,
  1469. * which are AccessibleContext.ACCESSIBLE_CARET_PROPERTY and
  1470. * AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY).
  1471. * This keeps track of the dot position internally. When the caret
  1472. * moves, the internal position is updated after firing the event.
  1473. *
  1474. * @param e the CaretEvent
  1475. */
  1476. public void caretUpdate(CaretEvent e) {
  1477. int dot = e.getDot();
  1478. int mark = e.getMark();
  1479. if (caretPos != dot) {
  1480. // the caret moved
  1481. firePropertyChange(ACCESSIBLE_CARET_PROPERTY,
  1482. new Integer(caretPos), new Integer(dot));
  1483. caretPos = dot;
  1484. }
  1485. if (mark != dot) {
  1486. // there is a selection
  1487. firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
  1488. getSelectedText());
  1489. }
  1490. }
  1491. // DocumentListener methods
  1492. /**
  1493. * Handles document insert (fire appropriate property change event
  1494. * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
  1495. * This tracks the changed offset via the event.
  1496. *
  1497. * @param e the DocumentEvent
  1498. */
  1499. public void insertUpdate(DocumentEvent e) {
  1500. final Integer pos = new Integer (e.getOffset());
  1501. if (SwingUtilities.isEventDispatchThread()) {
  1502. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
  1503. } else {
  1504. Runnable doFire = new Runnable() {
  1505. public void run() {
  1506. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
  1507. null, pos);
  1508. }
  1509. };
  1510. SwingUtilities.invokeLater(doFire);
  1511. }
  1512. }
  1513. /**
  1514. * Handles document remove (fire appropriate property change event,
  1515. * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
  1516. * This tracks the changed offset via the event.
  1517. *
  1518. * @param e the DocumentEvent
  1519. */
  1520. public void removeUpdate(DocumentEvent e) {
  1521. final Integer pos = new Integer (e.getOffset());
  1522. if (SwingUtilities.isEventDispatchThread()) {
  1523. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
  1524. } else {
  1525. Runnable doFire = new Runnable() {
  1526. public void run() {
  1527. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
  1528. null, pos);
  1529. }
  1530. };
  1531. SwingUtilities.invokeLater(doFire);
  1532. }
  1533. }
  1534. /**
  1535. * Handles document remove (fire appropriate property change event,
  1536. * which is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY).
  1537. * This tracks the changed offset via the event.
  1538. *
  1539. * @param e the DocumentEvent
  1540. */
  1541. public void changedUpdate(DocumentEvent e) {
  1542. final Integer pos = new Integer (e.getOffset());
  1543. if (SwingUtilities.isEventDispatchThread()) {
  1544. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, pos);
  1545. } else {
  1546. Runnable doFire = new Runnable() {
  1547. public void run() {
  1548. firePropertyChange(ACCESSIBLE_TEXT_PROPERTY,
  1549. null, pos);
  1550. }
  1551. };
  1552. SwingUtilities.invokeLater(doFire);
  1553. }
  1554. }
  1555. /**
  1556. * Gets the state set of the JTextComponent.
  1557. * The AccessibleStateSet of an object is composed of a set of
  1558. * unique AccessibleState's. A change in the AccessibleStateSet
  1559. * of an object will cause a PropertyChangeEvent to be fired
  1560. * for the AccessibleContext.ACCESSIBLE_STATE_PROPERTY property.
  1561. *
  1562. * @return an instance of AccessibleStateSet containing the
  1563. * current state set of the object
  1564. * @see AccessibleStateSet
  1565. * @see AccessibleState
  1566. * @see #addPropertyChangeListener
  1567. */
  1568. public AccessibleStateSet getAccessibleStateSet() {
  1569. AccessibleStateSet states = super.getAccessibleStateSet();
  1570. if (JTextComponent.this.isEditable()) {
  1571. states.add(AccessibleState.EDITABLE);
  1572. }
  1573. return states;
  1574. }
  1575. /**
  1576. * Gets the role of this object.
  1577. *
  1578. * @return an instance of AccessibleRole describing the role of the
  1579. * object (AccessibleRole.TEXT)
  1580. * @see AccessibleRole
  1581. */
  1582. public AccessibleRole getAccessibleRole() {
  1583. return AccessibleRole.TEXT;
  1584. }
  1585. /**
  1586. * Get the AccessibleText associated with this object. In the
  1587. * implementation of the Java Accessibility API for this class,
  1588. * return this object, which is responsible for implementing the
  1589. * AccessibleText interface on behalf of itself.
  1590. *
  1591. * @return this object
  1592. */
  1593. public AccessibleText getAccessibleText() {
  1594. return this;
  1595. }
  1596. // --- interface AccessibleText methods ------------------------
  1597. /**
  1598. * Many of these methods are just convenience methods; they
  1599. * just call the equivalent on the parent
  1600. */
  1601. /**
  1602. * Given a point in local coordinates, return the zero-based index
  1603. * of the character under that Point. If the point is invalid,
  1604. * this method returns -1.
  1605. *
  1606. * @param p the Point in local coordinates
  1607. * @return the zero-based index of the character under Point p.
  1608. */
  1609. public int getIndexAtPoint(Point p) {
  1610. if (p == null) {
  1611. return -1;
  1612. }
  1613. return JTextComponent.this.viewToModel(p);
  1614. }
  1615. /**
  1616. * Gets the editor's drawing rectangle. Stolen
  1617. * from the unfortunately named
  1618. * BasicTextUI.getVisibleEditorRect()
  1619. *
  1620. * @return the bounding box for the root view
  1621. */
  1622. Rectangle getRootEditorRect() {
  1623. Rectangle alloc = JTextComponent.this.getBounds();
  1624. if ((alloc.width > 0) && (alloc.height > 0)) {
  1625. alloc.x = alloc.y = 0;
  1626. Insets insets = JTextComponent.this.getInsets();
  1627. alloc.x += insets.left;
  1628. alloc.y += insets.top;
  1629. alloc.width -= insets.left + insets.right;
  1630. alloc.height -= insets.top + insets.bottom;
  1631. return alloc;
  1632. }
  1633. return null;
  1634. }
  1635. /**
  1636. * Determines the bounding box of the character at the given
  1637. * index into the string. The bounds are returned in local
  1638. * coordinates. If the index is invalid a null rectangle
  1639. * is returned.
  1640. *
  1641. * Note: the JTextComponent must have a valid size (e.g. have
  1642. * been added to a parent container whose ancestor container
  1643. * is a valid top-level window) for this method to be able
  1644. * to return a meaningful (non-null) value.
  1645. *
  1646. * @param i the index into the String >= 0
  1647. * @return the screen coordinates of the character's bounding box
  1648. */
  1649. public Rectangle getCharacterBounds(int i) {
  1650. if (i < 0 || i > model.getLength()-1) {
  1651. return null;
  1652. }
  1653. TextUI ui = getUI();
  1654. if (ui == null) {
  1655. return null;
  1656. }
  1657. Rectangle rect = null;
  1658. Rectangle alloc = getRootEditorRect();
  1659. if (alloc == null) {
  1660. return null;
  1661. }
  1662. if (model instanceof AbstractDocument) {
  1663. ((AbstractDocument)model).readLock();
  1664. }
  1665. try {
  1666. View rootView = ui.getRootView(JTextComponent.this);
  1667. if (rootView != null) {
  1668. rootView.setSize(alloc.width, alloc.height);
  1669. Shape bounds = rootView.modelToView(i,
  1670. Position.Bias.Forward, i+1,
  1671. Position.Bias.Backward, alloc);
  1672. rect = (bounds instanceof Rectangle) ?
  1673. (Rectangle)bounds : bounds.getBounds();
  1674. }
  1675. } catch (BadLocationException e) {
  1676. } finally {
  1677. if (model instanceof AbstractDocument) {
  1678. ((AbstractDocument)model).readUnlock();
  1679. }
  1680. }
  1681. return rect;
  1682. }
  1683. /**
  1684. * Returns the number of characters (valid indicies)
  1685. *
  1686. * @return the number of characters >= 0
  1687. */
  1688. public int getCharCount() {
  1689. return model.getLength();
  1690. }
  1691. /**
  1692. * Returns the zero-based offset of the caret.
  1693. *
  1694. * Note: The character to the right of the caret will have the
  1695. * same index value as the offset (the caret is between
  1696. * two characters).
  1697. *
  1698. * @return the zero-based offset of the caret.
  1699. */
  1700. public int getCaretPosition() {
  1701. return JTextComponent.this.getCaretPosition();
  1702. }
  1703. /**
  1704. * Returns the AttributeSet for a given character (at a given index).
  1705. *
  1706. * @param i the zero-based index into the text
  1707. * @return the AttributeSet of the character
  1708. */
  1709. public AttributeSet getCharacterAttribute(int i) {
  1710. Element e = null;
  1711. if (model instanceof AbstractDocument) {
  1712. ((AbstractDocument)model).readLock();
  1713. }
  1714. try {
  1715. for (e = model.getDefaultRootElement(); ! e.isLeaf(); ) {
  1716. int index = e.getElementIndex(i);
  1717. e = e.getElement(index);
  1718. }
  1719. } finally {
  1720. if (model instanceof AbstractDocument) {
  1721. ((AbstractDocument)model).readUnlock();
  1722. }
  1723. }
  1724. return e.getAttributes();
  1725. }
  1726. /**
  1727. * Returns the start offset within the selected text.
  1728. * If there is no selection, but there is
  1729. * a caret, the start and end offsets will be the same.
  1730. * Return 0 if the text is empty, or the caret position
  1731. * if no selection.
  1732. *
  1733. * @return the index into the text of the start of the selection >= 0
  1734. */
  1735. public int getSelectionStart() {
  1736. return JTextComponent.this.getSelectionStart();
  1737. }
  1738. /**
  1739. * Returns the end offset within the selected text.
  1740. * If there is no selection, but there is
  1741. * a caret, the start and end offsets will be the same.
  1742. * Return 0 if the text is empty, or the caret position
  1743. * if no selection.
  1744. *
  1745. * @return the index into teh text of the end of the selection >= 0
  1746. */
  1747. public int getSelectionEnd() {
  1748. return JTextComponent.this.getSelectionEnd();
  1749. }
  1750. /**
  1751. * Returns the portion of the text that is selected.
  1752. *
  1753. * @return the text, null if no selection
  1754. */
  1755. public String getSelectedText() {
  1756. return JTextComponent.this.getSelectedText();
  1757. }
  1758. /**
  1759. * Returns the String at a given index.
  1760. *
  1761. * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
  1762. * or AccessibleText.SENTENCE to retrieve
  1763. * @param index an index within the text >= 0
  1764. * @return the letter, word, or sentence,
  1765. * null for an invalid index or part
  1766. */
  1767. public String getAtIndex(int part, int index) {
  1768. if (index < 0 || index >= model.getLength()) {
  1769. return null;
  1770. }
  1771. String str = null;
  1772. String s = null;
  1773. int end;
  1774. if (model instanceof AbstractDocument) {
  1775. ((AbstractDocument)model).readLock();
  1776. }
  1777. try {
  1778. switch (part) {
  1779. case AccessibleText.CHARACTER:
  1780. str = model.getText(index, 1);
  1781. break;
  1782. case AccessibleText.WORD:
  1783. s = model.getText(0, model.getLength());
  1784. BreakIterator words = BreakIterator.getWordInstance();
  1785. words.setText(s);
  1786. end = words.following(index);
  1787. str = s.substring(words.previous(), end);
  1788. break;
  1789. case AccessibleText.SENTENCE:
  1790. s = model.getText(0, model.getLength());
  1791. BreakIterator sentence = BreakIterator.getSentenceInstance();
  1792. sentence.setText(s);
  1793. end = sentence.following(index);
  1794. str = s.substring(sentence.previous(), end);
  1795. break;
  1796. default:
  1797. }
  1798. } catch (BadLocationException e) {
  1799. } finally {
  1800. if (model instanceof AbstractDocument) {
  1801. ((AbstractDocument)model).readUnlock();
  1802. }
  1803. }
  1804. return str;
  1805. }
  1806. /**
  1807. * Returns the String after a given index.
  1808. *
  1809. * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
  1810. * or AccessibleText.SENTENCE to retrieve
  1811. * @param index an index within the text >= 0
  1812. * @return the letter, word, or sentence, null for an invalid
  1813. * index or part
  1814. */
  1815. public String getAfterIndex(int part, int index) {
  1816. if (index < 0 || index >= model.getLength()) {
  1817. return null;
  1818. }
  1819. String str = null;
  1820. String s = null;
  1821. int start;
  1822. int end;
  1823. if (model instanceof AbstractDocument) {
  1824. ((AbstractDocument)model).readLock();
  1825. }
  1826. try {
  1827. switch (part) {
  1828. case AccessibleText.CHARACTER:
  1829. if (index+1 < model.getLength()) {
  1830. str = model.getText(index+1, 1);
  1831. }
  1832. break;
  1833. case AccessibleText.WORD:
  1834. s = model.getText(0, model.getLength());
  1835. BreakIterator words = BreakIterator.getWordInstance();
  1836. words.setText(s);
  1837. start = words.following(index);
  1838. if (start == BreakIterator.DONE || start >= s.length()) {
  1839. break;
  1840. }
  1841. end = words.following(start);
  1842. if (end == BreakIterator.DONE || end >= s.length()) {
  1843. break;
  1844. }
  1845. str = s.substring(start, end);
  1846. break;
  1847. case AccessibleText.SENTENCE:
  1848. s = model.getText(0, model.getLength());
  1849. BreakIterator sentence = BreakIterator.getSentenceInstance();
  1850. sentence.setText(s);
  1851. start = sentence.following(index);
  1852. if (start == BreakIterator.DONE || start >= s.length()) {
  1853. break;
  1854. }
  1855. end = sentence.following(start);
  1856. if (end == BreakIterator.DONE || end >= s.length()) {
  1857. break;
  1858. }
  1859. str = s.substring(start, end);
  1860. break;
  1861. default:
  1862. return null;
  1863. }
  1864. } catch (BadLocationException e) {
  1865. } finally {
  1866. if (model instanceof AbstractDocument) {
  1867. ((AbstractDocument)model).readUnlock();
  1868. }
  1869. }
  1870. return str;
  1871. }
  1872. /**
  1873. * Returns the String before a given index.
  1874. *
  1875. * @param part the AccessibleText.CHARACTER, AccessibleText.WORD,
  1876. * or AccessibleText.SENTENCE to retrieve
  1877. * @param index an index within the text >= 0
  1878. * @return the letter, word, or sentence, null for an invalid index
  1879. * or part
  1880. */
  1881. public String getBeforeIndex(int part, int index) {
  1882. if (index < 0 || index > model.getLength()-1) {
  1883. return null;
  1884. }
  1885. String str = null;
  1886. String s = null;
  1887. int start;
  1888. int end;
  1889. if (model instanceof AbstractDocument) {
  1890. ((AbstractDocument)model).readLock();
  1891. }
  1892. try {
  1893. switch (part) {
  1894. case AccessibleText.CHARACTER:
  1895. if (index != 0) {
  1896. str = model.getText(index-1, 1);
  1897. }
  1898. break;
  1899. case AccessibleText.WORD:
  1900. s = model.getText(0, model.getLength());
  1901. BreakIterator words = BreakIterator.getWordInstance();
  1902. words.setText(s);
  1903. end = words.following(index);
  1904. end = words.previous();
  1905. start = words.previous();
  1906. if (start != BreakIterator.DONE) {
  1907. str = s.substring(start, end);
  1908. }
  1909. break;
  1910. case AccessibleText.SENTENCE:
  1911. s = model.getText(0, model.getLength());
  1912. BreakIterator sentence = BreakIterator.getSentenceInstance();
  1913. sentence.setText(s);
  1914. end = sentence.following(index);
  1915. end = sentence.previous();
  1916. start = sentence.previous();
  1917. if (start != BreakIterator.DONE) {
  1918. str =s.substring(start, end);
  1919. }
  1920. break;
  1921. default:
  1922. return null;
  1923. }
  1924. } catch (BadLocationException e) {
  1925. } finally {
  1926. if (model instanceof AbstractDocument) {
  1927. ((AbstractDocument)model).readUnlock();
  1928. }
  1929. }
  1930. return str;
  1931. }
  1932. }
  1933. // --- serialization ---------------------------------------------
  1934. private void readObject(ObjectInputStream s)
  1935. throws IOException, ClassNotFoundException
  1936. {
  1937. s.defaultReadObject();
  1938. caretEvent = new MutableCaretEvent(this);
  1939. addMouseListener(caretEvent);
  1940. addFocusListener(caretEvent);
  1941. }
  1942. // --- member variables ----------------------------------
  1943. /**
  1944. * The document model.
  1945. */
  1946. private Document model;
  1947. /**
  1948. * The caret used to display the insert position
  1949. * and navigate throught the document.
  1950. *
  1951. * PENDING(prinz)
  1952. * This should be serializable, default installed
  1953. * by UI.
  1954. */
  1955. private transient Caret caret;
  1956. /**
  1957. * The object responsible for managing highlights.
  1958. *
  1959. * PENDING(prinz)
  1960. * This should be serializable, default installed
  1961. * by UI.
  1962. */
  1963. private transient Highlighter highlighter;
  1964. /**
  1965. * The current key bindings in effect.
  1966. *
  1967. * PENDING(prinz)
  1968. * This should be serializable, default installed
  1969. * by UI.
  1970. */
  1971. private transient Keymap keymap;
  1972. private transient MutableCaretEvent caretEvent;
  1973. private Color caretColor;
  1974. private Color selectionColor;
  1975. private Color selectedTextColor;
  1976. private Color disabledTextColor;
  1977. private boolean editable;
  1978. private Insets margin;
  1979. private char focusAccelerator;
  1980. /**
  1981. * Indicates if it is safe to access the system clipboard. Once false,
  1982. * access will never be checked again.
  1983. */
  1984. private boolean canAccessSystemClipboard;
  1985. /**
  1986. * Key used in app context to lookup Clipboard to use if access to
  1987. * System clipboard is denied.
  1988. */
  1989. private static Object SandboxClipboardKey = new Object();
  1990. private static ClipboardOwner defaultClipboardOwner = new ClipboardObserver();
  1991. /**
  1992. * Returns a string representation of this JTextComponent. This method
  1993. * is intended to be used only for debugging purposes, and the
  1994. * content and format of the returned string may vary between
  1995. * implementations. The returned string may be empty but may not
  1996. * be <code>null</code>.
  1997. * <P>
  1998. * Overriding paramString() to provide information about the
  1999. * specific new aspects of the JFC components.
  2000. *
  2001. * @return a string representation of this JTextComponent.
  2002. */
  2003. protected String paramString() {
  2004. String editableString = (editable ?
  2005. "true" : "false");
  2006. String caretColorString = (caretColor != null ?
  2007. caretColor.toString() : "");
  2008. String selectionColorString = (selectionColor != null ?
  2009. selectionColor.toString() : "");
  2010. String selectedTextColorString = (selectedTextColor != null ?
  2011. selectedTextColor.toString() : "");
  2012. String disabledTextColorString = (disabledTextColor != null ?
  2013. disabledTextColor.toString() : "");
  2014. String marginString = (margin != null ?
  2015. margin.toString() : "");
  2016. return super.paramString() +
  2017. ",caretColor=" + caretColorString +
  2018. ",disabledTextColor=" + disabledTextColorString +
  2019. ",editable=" + editableString +
  2020. ",margin=" + marginString +
  2021. ",selectedTextColor=" + selectedTextColorString +
  2022. ",selectionColor=" + selectionColorString;
  2023. }
  2024. static class ClipboardObserver implements ClipboardOwner {
  2025. public void lostOwnership(Clipboard clipboard, Transferable contents) {
  2026. }
  2027. }
  2028. /**
  2029. * package level access to focused text component
  2030. * so that JTextAction implementations can be
  2031. * reused across JTextComponent implementations.
  2032. */
  2033. static final JTextComponent getFocusedComponent() {
  2034. return focusedComponent;
  2035. }
  2036. private static Hashtable keymapTable = null;
  2037. private JTextComponent editor;
  2038. private static JTextComponent focusedComponent;
  2039. //
  2040. // member variables used for on-the-spot input method
  2041. // editing style support
  2042. //
  2043. private InputMethodRequests inputMethodRequestsHandler;
  2044. private AttributedString composedText;
  2045. private String composedTextContent;
  2046. private Position composedTextStart;
  2047. private Position composedTextEnd;
  2048. private ComposedTextCaret composedTextCaret;
  2049. private transient Caret originalCaret;
  2050. private boolean needToSendKeyTypedEvent;
  2051. static class DefaultKeymap implements Keymap {
  2052. DefaultKeymap(String nm, Keymap parent) {
  2053. this.nm = nm;
  2054. this.parent = parent;
  2055. bindings = new Hashtable();
  2056. }
  2057. /**
  2058. * Fetch the default action to fire if a
  2059. * key is typed (ie a KEY_TYPED KeyEvent is received)
  2060. * and there is no binding for it. Typically this
  2061. * would be some action that inserts text so that
  2062. * the keymap doesn't require an action for each
  2063. * possible key.
  2064. */
  2065. public Action getDefaultAction() {
  2066. if (defaultAction != null) {
  2067. return defaultAction;
  2068. }
  2069. return (parent != null) ? parent.getDefaultAction() : null;
  2070. }
  2071. /**
  2072. * Set the default action to fire if a key is typed.
  2073. */
  2074. public void setDefaultAction(Action a) {
  2075. defaultAction = a;
  2076. }
  2077. public String getName() {
  2078. return nm;
  2079. }
  2080. public Action getAction(KeyStroke key) {
  2081. Action a = (Action) bindings.get(key);
  2082. if ((a == null) && (parent != null)) {
  2083. a = parent.getAction(key);
  2084. }
  2085. return a;
  2086. }
  2087. public KeyStroke[] getBoundKeyStrokes() {
  2088. KeyStroke[] keys = new KeyStroke[bindings.size()];
  2089. int i = 0;
  2090. for (Enumeration e = bindings.keys() ; e.hasMoreElements() ;) {
  2091. keys[i++] = (KeyStroke) e.nextElement();
  2092. }
  2093. return keys;
  2094. }
  2095. public Action[] getBoundActions() {
  2096. Action[] actions = new Action[bindings.size()];
  2097. int i = 0;
  2098. for (Enumeration e = bindings.elements() ; e.hasMoreElements() ;) {
  2099. actions[i++] = (Action) e.nextElement();
  2100. }
  2101. return actions;
  2102. }
  2103. public KeyStroke[] getKeyStrokesForAction(Action a) {
  2104. if (a == null) {
  2105. return null;
  2106. }
  2107. KeyStroke[] retValue = null;
  2108. // Determine local bindings first.
  2109. Vector keyStrokes = null;
  2110. for (Enumeration enum = bindings.keys();
  2111. enum.hasMoreElements();) {
  2112. Object key = enum.nextElement();
  2113. if (bindings.get(key) == a) {
  2114. if (keyStrokes == null) {
  2115. keyStrokes = new Vector();
  2116. }
  2117. keyStrokes.addElement(key);
  2118. }
  2119. }
  2120. // See if the parent has any.
  2121. if (parent != null) {
  2122. KeyStroke[] pStrokes = parent.getKeyStrokesForAction(a);
  2123. if (pStrokes != null) {
  2124. // Remove any bindings defined in the parent that
  2125. // are locally defined.
  2126. int rCount = 0;
  2127. for (int counter = pStrokes.length - 1; counter >= 0;
  2128. counter--) {
  2129. if (isLocallyDefined(pStrokes[counter])) {
  2130. pStrokes[counter] = null;
  2131. rCount++;
  2132. }
  2133. }
  2134. if (rCount > 0 && rCount < pStrokes.length) {
  2135. if (keyStrokes == null) {
  2136. keyStrokes = new Vector();
  2137. }
  2138. for (int counter = pStrokes.length - 1; counter >= 0;
  2139. counter--) {
  2140. if (pStrokes[counter] != null) {
  2141. keyStrokes.addElement(pStrokes[counter]);
  2142. }
  2143. }
  2144. }
  2145. else if (rCount == 0) {
  2146. if (keyStrokes == null) {
  2147. retValue = pStrokes;
  2148. }
  2149. else {
  2150. retValue = new KeyStroke[keyStrokes.size() +
  2151. pStrokes.length];
  2152. keyStrokes.copyInto(retValue);
  2153. System.arraycopy(pStrokes, 0, retValue,
  2154. keyStrokes.size(), pStrokes.length);
  2155. keyStrokes = null;
  2156. }
  2157. }
  2158. }
  2159. }
  2160. if (keyStrokes != null) {
  2161. retValue = new KeyStroke[keyStrokes.size()];
  2162. keyStrokes.copyInto(retValue);
  2163. }
  2164. return retValue;
  2165. }
  2166. public boolean isLocallyDefined(KeyStroke key) {
  2167. return bindings.containsKey(key);
  2168. }
  2169. public void addActionForKeyStroke(KeyStroke key, Action a) {
  2170. bindings.put(key, a);
  2171. }
  2172. public void removeKeyStrokeBinding(KeyStroke key) {
  2173. bindings.remove(key);
  2174. }
  2175. public void removeBindings() {
  2176. bindings.clear();
  2177. }
  2178. public Keymap getResolveParent() {
  2179. return parent;
  2180. }
  2181. public void setResolveParent(Keymap parent) {
  2182. this.parent = parent;
  2183. }
  2184. /**
  2185. * String representation of the keymap... potentially
  2186. * a very long string.
  2187. */
  2188. public String toString() {
  2189. return "Keymap[" + nm + "]" + bindings;
  2190. }
  2191. String nm;
  2192. Keymap parent;
  2193. Hashtable bindings;
  2194. Action defaultAction;
  2195. }
  2196. /**
  2197. * KeymapWrapper wraps a Keymap inside an InputMap. For KeymapWrapper
  2198. * to be useful it must be used with a KeymapActionMap.
  2199. * KeymapWrapper for the most part, is an InputMap with two parents.
  2200. * The first parent visited is ALWAYS the Keymap, with the second
  2201. * parent being the parent inherited from InputMap. If
  2202. * <code>keymap.getAction</code> returns null, implying the Keymap
  2203. * does not have a binding for the KeyStroke,
  2204. * the parent is then visited. If the Keymap has a binding, the
  2205. * Action is returned, if not and the KeyStroke represents a
  2206. * KeyTyped event and the Keymap has a defaultAction,
  2207. * <code>DefaultActionKey</code> is returned.
  2208. * <p>KeymapActionMap is then able to transate the object passed in
  2209. * to either message the Keymap, or message its default implementation.
  2210. */
  2211. static class KeymapWrapper extends InputMap {
  2212. static final Object DefaultActionKey = new Object();
  2213. private Keymap keymap;
  2214. KeymapWrapper(Keymap keymap) {
  2215. this.keymap = keymap;
  2216. }
  2217. public KeyStroke[] keys() {
  2218. KeyStroke[] sKeys = super.keys();
  2219. KeyStroke[] keymapKeys = keymap.getBoundKeyStrokes();
  2220. int sCount = (sKeys == null) ? 0 : sKeys.length;
  2221. int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
  2222. if (sCount == 0) {
  2223. return keymapKeys;
  2224. }
  2225. if (keymapCount == 0) {
  2226. return sKeys;
  2227. }
  2228. KeyStroke[] retValue = new KeyStroke[sCount + keymapCount];
  2229. // There may be some duplication here...
  2230. System.arraycopy(sKeys, 0, retValue, 0, sCount);
  2231. System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
  2232. return retValue;
  2233. }
  2234. public int size() {
  2235. // There may be some duplication here...
  2236. KeyStroke[] keymapStrokes = keymap.getBoundKeyStrokes();
  2237. int keymapCount = (keymapStrokes == null) ? 0:
  2238. keymapStrokes.length;
  2239. return super.size() + keymapCount;
  2240. }
  2241. public Object get(KeyStroke keyStroke) {
  2242. Object retValue = keymap.getAction(keyStroke);
  2243. if (retValue == null) {
  2244. retValue = super.get(keyStroke);
  2245. if (retValue == null && keyStroke.getKeyChar() != 0 &&
  2246. keymap.getDefaultAction() != null) {
  2247. // Implies this is a KeyTyped event, use the default
  2248. // action.
  2249. retValue = DefaultActionKey;
  2250. }
  2251. }
  2252. return retValue;
  2253. }
  2254. }
  2255. /**
  2256. * Wraps a Keymap inside an ActionMap. This is used with
  2257. * a KeymapWrapper. If <code>get</code> is passed in
  2258. * <code>KeymapWrapper.DefaultActionKey</code>, the default action is
  2259. * returned, otherwise if the key is an Action, it is returned.
  2260. */
  2261. static class KeymapActionMap extends ActionMap {
  2262. private Keymap keymap;
  2263. KeymapActionMap(Keymap keymap) {
  2264. this.keymap = keymap;
  2265. }
  2266. public Object[] keys() {
  2267. Object[] sKeys = super.keys();
  2268. Object[] keymapKeys = keymap.getBoundActions();
  2269. int sCount = (sKeys == null) ? 0 : sKeys.length;
  2270. int keymapCount = (keymapKeys == null) ? 0 : keymapKeys.length;
  2271. boolean hasDefault = (keymap.getDefaultAction() != null);
  2272. if (hasDefault) {
  2273. keymapCount++;
  2274. }
  2275. if (sCount == 0) {
  2276. if (hasDefault) {
  2277. Object[] retValue = new Object[keymapCount];
  2278. if (keymapCount > 1) {
  2279. System.arraycopy(keymapKeys, 0, retValue, 0,
  2280. keymapCount - 1);
  2281. }
  2282. retValue[keymapCount - 1] = KeymapWrapper.DefaultActionKey;
  2283. return retValue;
  2284. }
  2285. return keymapKeys;
  2286. }
  2287. if (keymapCount == 0) {
  2288. return sKeys;
  2289. }
  2290. Object[] retValue = new Object[sCount + keymapCount];
  2291. // There may be some duplication here...
  2292. System.arraycopy(sKeys, 0, retValue, 0, sCount);
  2293. if (hasDefault) {
  2294. if (keymapCount > 1) {
  2295. System.arraycopy(keymapKeys, 0, retValue, sCount,
  2296. keymapCount - 1);
  2297. }
  2298. retValue[sCount + keymapCount - 1] = KeymapWrapper.
  2299. DefaultActionKey;
  2300. }
  2301. else {
  2302. System.arraycopy(keymapKeys, 0, retValue, sCount, keymapCount);
  2303. }
  2304. return retValue;
  2305. }
  2306. public int size() {
  2307. // There may be some duplication here...
  2308. Object[] actions = keymap.getBoundActions();
  2309. int keymapCount = (actions == null) ? 0 : actions.length;
  2310. if (keymap.getDefaultAction() != null) {
  2311. keymapCount++;
  2312. }
  2313. return super.size() + keymapCount;
  2314. }
  2315. public Action get(Object key) {
  2316. Action retValue = super.get(key);
  2317. if (retValue == null) {
  2318. // Try the Keymap.
  2319. if (key == KeymapWrapper.DefaultActionKey) {
  2320. retValue = keymap.getDefaultAction();
  2321. }
  2322. else if (key instanceof Action) {
  2323. // This is a little iffy, technically an Action is
  2324. // a valid Key. We're assuming the Action came from
  2325. // the InputMap though.
  2326. retValue = (Action)key;
  2327. }
  2328. }
  2329. return retValue;
  2330. }
  2331. }
  2332. /**
  2333. * This is the name of the default keymap that will be shared by all
  2334. * JTextComponent instances unless they have had a different
  2335. * keymap set.
  2336. */
  2337. public static final String DEFAULT_KEYMAP = "default";
  2338. static {
  2339. try {
  2340. keymapTable = new Hashtable(17);
  2341. Keymap binding = addKeymap(DEFAULT_KEYMAP, null);
  2342. binding.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction());
  2343. } catch (Throwable e) {
  2344. e.printStackTrace();
  2345. }
  2346. }
  2347. /**
  2348. * Event to use when firing a notification of change to caret
  2349. * position. This is mutable so that the event can be reused
  2350. * since caret events can be fairly high in bandwidth.
  2351. */
  2352. static class MutableCaretEvent extends CaretEvent implements ChangeListener, MouseListener, FocusListener {
  2353. MutableCaretEvent(JTextComponent c) {
  2354. super(c);
  2355. }
  2356. final void fire() {
  2357. JTextComponent c = (JTextComponent) getSource();
  2358. if (c != null) {
  2359. Caret caret = c.getCaret();
  2360. dot = caret.getDot();
  2361. mark = caret.getMark();
  2362. c.fireCaretUpdate(this);
  2363. }
  2364. }
  2365. public final String toString() {
  2366. return "dot=" + dot + "," + "mark=" + mark;
  2367. }
  2368. // --- CaretEvent methods -----------------------
  2369. public final int getDot() {
  2370. return dot;
  2371. }
  2372. public final int getMark() {
  2373. return mark;
  2374. }
  2375. // --- ChangeListener methods -------------------
  2376. public final void stateChanged(ChangeEvent e) {
  2377. if (! dragActive) {
  2378. fire();
  2379. }
  2380. }
  2381. // --- FocusListener methods --------------------------------
  2382. /**
  2383. * Stashes the current focused JTextComponent reference
  2384. * for JTextAction instances to use if the ActionEvent
  2385. * doesn't contain the target text component.
  2386. *
  2387. * @param e the focus event
  2388. * @see JTextAction
  2389. * @see FocusListener#focusGained
  2390. */
  2391. public void focusGained(FocusEvent e) {
  2392. focusedComponent = (JTextComponent) getSource();
  2393. }
  2394. /**
  2395. * Removes reference to focused text component that
  2396. * instances of JTextAction use.
  2397. *
  2398. * @param e the focus event
  2399. * @see JTextAction
  2400. * @see FocusListener#focusLost
  2401. */
  2402. public void focusLost(FocusEvent e) {
  2403. // temp focus loss from menus causes problems
  2404. //focusedComponent = null;
  2405. }
  2406. // --- MouseListener methods -----------------------------------
  2407. /**
  2408. * Requests focus on the associated
  2409. * text component, and try to set the cursor position.
  2410. *
  2411. * @param e the mouse event
  2412. * @see MouseListener#mousePressed
  2413. */
  2414. public final void mousePressed(MouseEvent e) {
  2415. dragActive = true;
  2416. }
  2417. /**
  2418. * Called when the mouse is released.
  2419. *
  2420. * @param e the mouse event
  2421. * @see MouseListener#mouseReleased
  2422. */
  2423. public final void mouseReleased(MouseEvent e) {
  2424. dragActive = false;
  2425. fire();
  2426. }
  2427. public final void mouseClicked(MouseEvent e) {
  2428. }
  2429. public final void mouseEntered(MouseEvent e) {
  2430. }
  2431. public final void mouseExited(MouseEvent e) {
  2432. }
  2433. private boolean dragActive;
  2434. private int dot;
  2435. private int mark;
  2436. }
  2437. //
  2438. // Process any input method events that the component itself
  2439. // recognizes. The default on-the-spot handling for input method
  2440. // composed(uncommitted) text is done here after all input
  2441. // method listeners get called for stealing the events.
  2442. //
  2443. protected void processInputMethodEvent(InputMethodEvent e) {
  2444. // let listeners handle the events
  2445. super.processInputMethodEvent(e);
  2446. if (!e.isConsumed()) {
  2447. if (! isEditable()) {
  2448. return;
  2449. } else {
  2450. switch (e.getID()) {
  2451. case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED:
  2452. replaceInputMethodText(e);
  2453. // fall through
  2454. case InputMethodEvent.CARET_POSITION_CHANGED:
  2455. setInputMethodCaretPosition(e);
  2456. break;
  2457. }
  2458. }
  2459. e.consume();
  2460. }
  2461. }
  2462. //
  2463. // Overrides this method to become an active input method client.
  2464. //
  2465. public InputMethodRequests getInputMethodRequests() {
  2466. if (inputMethodRequestsHandler == null) {
  2467. inputMethodRequestsHandler =
  2468. (InputMethodRequests)new InputMethodRequestsHandler();
  2469. }
  2470. return inputMethodRequestsHandler;
  2471. }
  2472. //
  2473. // Overrides this method to watch the listener installed.
  2474. //
  2475. public void addInputMethodListener(InputMethodListener l) {
  2476. super.addInputMethodListener(l);
  2477. if (l != null) {
  2478. needToSendKeyTypedEvent = false;
  2479. }
  2480. }
  2481. //
  2482. // Default implementation of the InputMethodRequests interface.
  2483. //
  2484. class InputMethodRequestsHandler implements InputMethodRequests, Serializable {
  2485. public AttributedCharacterIterator cancelLatestCommittedText(
  2486. Attribute[] attributes) {
  2487. return new AttributedString("").getIterator();
  2488. }
  2489. public AttributedCharacterIterator getCommittedText(int beginIndex,
  2490. int endIndex, Attribute[] attributes) {
  2491. return new AttributedString("").getIterator();
  2492. }
  2493. public int getCommittedTextLength() {
  2494. return 0;
  2495. }
  2496. public int getInsertPositionOffset() {
  2497. return getCaretPosition();
  2498. }
  2499. public TextHitInfo getLocationOffset(int x, int y) {
  2500. if (composedText == null) {
  2501. return null;
  2502. } else {
  2503. Point p = getLocationOnScreen();
  2504. p.x = x - p.x;
  2505. p.y = y - p.y;
  2506. int pos = viewToModel(p);
  2507. if ((pos >= composedTextStart.getOffset()) && (pos <= composedTextEnd.getOffset())) {
  2508. return TextHitInfo.leading(pos - composedTextStart.getOffset());
  2509. } else {
  2510. return null;
  2511. }
  2512. }
  2513. }
  2514. public Rectangle getTextLocation(TextHitInfo offset) {
  2515. Rectangle r;
  2516. try {
  2517. r = modelToView(getCaretPosition());
  2518. if (r != null) {
  2519. Point p = getLocationOnScreen();
  2520. r.translate(p.x, p.y);
  2521. }
  2522. } catch (BadLocationException ble) {
  2523. r = null;
  2524. }
  2525. if (r == null)
  2526. r = new Rectangle();
  2527. return r;
  2528. }
  2529. public AttributedCharacterIterator getSelectedText(
  2530. Attribute[] attributes) {
  2531. String selection = JTextComponent.this.getSelectedText();
  2532. if (selection != null) {
  2533. return new AttributedString(selection).getIterator();
  2534. } else {
  2535. return null;
  2536. }
  2537. }
  2538. }
  2539. //
  2540. // Replaces the current input method (composed) text according to
  2541. // the passed input method event. This method also inserts the
  2542. // committed text into the document.
  2543. //
  2544. private void replaceInputMethodText(InputMethodEvent e) {
  2545. int commitCount = e.getCommittedCharacterCount();
  2546. AttributedCharacterIterator text = e.getText();
  2547. int composedTextIndex;
  2548. // old composed text deletion
  2549. Document doc = getDocument();
  2550. if (composedTextStart != null) {
  2551. try {
  2552. int removeOffs = composedTextStart.getOffset();
  2553. doc.remove(removeOffs, composedTextEnd.getOffset()-removeOffs);
  2554. } catch (BadLocationException ble) {}
  2555. composedTextStart = composedTextEnd = null;
  2556. composedText = null;
  2557. composedTextContent = null;
  2558. }
  2559. if (text != null) {
  2560. text.first();
  2561. // committed text insertion
  2562. if (commitCount > 0) {
  2563. // Need to generate KeyTyped events for the committed text for components
  2564. // that are not aware they are active input method clients.
  2565. if (needToSendKeyTypedEvent) {
  2566. for (char c = text.current(); commitCount > 0;
  2567. c = text.next(), commitCount--) {
  2568. KeyEvent ke = new KeyEvent(this, KeyEvent.KEY_TYPED,
  2569. System.currentTimeMillis(),
  2570. 0, KeyEvent.VK_UNDEFINED, c);
  2571. processKeyEvent(ke);
  2572. }
  2573. } else {
  2574. StringBuffer strBuf = new StringBuffer();
  2575. for (char c = text.current(); commitCount > 0;
  2576. c = text.next(), commitCount--) {
  2577. strBuf.append(c);
  2578. }
  2579. // map it to an ActionEvent
  2580. mapCommittedTextToAction(new String(strBuf));
  2581. }
  2582. }
  2583. // new composed text insertion
  2584. composedTextIndex = text.getIndex();
  2585. if (composedTextIndex < text.getEndIndex()) {
  2586. createComposedString(composedTextIndex, text);
  2587. SimpleAttributeSet attrSet = new SimpleAttributeSet();
  2588. attrSet.addAttribute(StyleConstants.ComposedTextAttribute,
  2589. composedText);
  2590. try {
  2591. replaceSelection(null);
  2592. doc.insertString(caret.getDot(), composedTextContent,
  2593. attrSet);
  2594. composedTextStart = doc.createPosition(caret.getDot() -
  2595. composedTextContent.length());
  2596. composedTextEnd = doc.createPosition(caret.getDot());
  2597. } catch (BadLocationException ble) {
  2598. composedTextStart = composedTextEnd = null;
  2599. composedText = null;
  2600. composedTextContent = null;
  2601. }
  2602. }
  2603. }
  2604. }
  2605. private void createComposedString(int composedIndex,
  2606. AttributedCharacterIterator text) {
  2607. Document doc = getDocument();
  2608. StringBuffer strBuf = new StringBuffer();
  2609. // create attributed string with no attributes
  2610. for (char c = text.setIndex(composedIndex);
  2611. c != CharacterIterator.DONE; c = text.next()) {
  2612. strBuf.append(c);
  2613. }
  2614. composedTextContent = new String(strBuf);
  2615. composedText = new AttributedString(text, composedIndex,
  2616. text.getEndIndex());
  2617. }
  2618. //
  2619. // Map committed text to an ActionEvent. If the committed text length is 1,
  2620. // treat it as a KeyStroke, otherwise or there is no KeyStroke defined,
  2621. // treat it just as a default action.
  2622. //
  2623. private void mapCommittedTextToAction(String committedText) {
  2624. Keymap binding = getKeymap();
  2625. if (binding != null) {
  2626. Action a = null;
  2627. if (committedText.length() == 1) {
  2628. KeyStroke k = KeyStroke.getKeyStroke(committedText.charAt(0));
  2629. a = binding.getAction(k);
  2630. }
  2631. if (a == null) {
  2632. a = binding.getDefaultAction();
  2633. }
  2634. if (a != null) {
  2635. ActionEvent ae = new ActionEvent(this,
  2636. ActionEvent.ACTION_PERFORMED,
  2637. committedText);
  2638. a.actionPerformed(ae);
  2639. }
  2640. }
  2641. }
  2642. //
  2643. // Sets the caret position according to the passed input method
  2644. // event. Also, sets/resets composed text caret appropriately.
  2645. //
  2646. private void setInputMethodCaretPosition(InputMethodEvent e) {
  2647. int dot;
  2648. if (composedTextStart != null) {
  2649. dot = composedTextStart.getOffset();
  2650. if (!(caret instanceof ComposedTextCaret)) {
  2651. if (composedTextCaret == null) {
  2652. composedTextCaret = new ComposedTextCaret();
  2653. }
  2654. originalCaret = caret;
  2655. // Sets composed text caret
  2656. exchangeCaret(originalCaret, composedTextCaret);
  2657. }
  2658. TextHitInfo caretPos = e.getCaret();
  2659. if (caretPos != null) {
  2660. dot += caretPos.getInsertionIndex();
  2661. }
  2662. caret.setDot(dot);
  2663. } else if (caret instanceof ComposedTextCaret) {
  2664. dot = caret.getDot();
  2665. // Restores original caret
  2666. exchangeCaret(caret, originalCaret);
  2667. caret.setDot(dot);
  2668. }
  2669. }
  2670. private void exchangeCaret(Caret oldCaret, Caret newCaret) {
  2671. int blinkRate = oldCaret.getBlinkRate();
  2672. setCaret(newCaret);
  2673. caret.setBlinkRate(blinkRate);
  2674. caret.setVisible(hasFocus());
  2675. }
  2676. //
  2677. // Checks whether the client code overrides processInputMethodEvent. If it is overridden,
  2678. // need not to generate KeyTyped events for committed text. If it's not, behave as an
  2679. // passive input method client.
  2680. //
  2681. private boolean isProcessInputMethodEventOverridden() {
  2682. Boolean ret = (Boolean)AccessController.doPrivileged(new PrivilegedAction() {
  2683. public Object run() {
  2684. Class[] classes = new Class[1];
  2685. classes[0] = InputMethodEvent.class;
  2686. for (Class c = JTextComponent.this.getClass();
  2687. c != JTextComponent.class; c = c.getSuperclass()) {
  2688. try {
  2689. Method m = c.getDeclaredMethod("processInputMethodEvent", classes);
  2690. return Boolean.TRUE;
  2691. } catch (NoSuchMethodException nsme) {
  2692. continue;
  2693. }
  2694. }
  2695. return Boolean.FALSE;
  2696. }
  2697. }
  2698. );
  2699. return ret.booleanValue();
  2700. }
  2701. //
  2702. // Caret implementation for editing the composed text.
  2703. //
  2704. class ComposedTextCaret extends DefaultCaret implements Serializable {
  2705. Color bg;
  2706. //
  2707. // Get the background color of the component
  2708. //
  2709. public void install(JTextComponent c) {
  2710. super.install(c);
  2711. Document doc = c.getDocument();
  2712. if (doc instanceof StyledDocument) {
  2713. StyledDocument sDoc = (StyledDocument)doc;
  2714. Element elem = sDoc.getCharacterElement(c.composedTextStart.getOffset());
  2715. AttributeSet attr = elem.getAttributes();
  2716. bg = sDoc.getBackground(attr);
  2717. }
  2718. if (bg == null) {
  2719. bg = c.getBackground();
  2720. }
  2721. }
  2722. //
  2723. // Draw caret in XOR mode.
  2724. //
  2725. public void paint(Graphics g) {
  2726. if(isVisible()) {
  2727. try {
  2728. Rectangle r = component.modelToView(getDot());
  2729. g.setXORMode(bg);
  2730. g.drawLine(r.x, r.y, r.x, r.y + r.height - 1);
  2731. g.setPaintMode();
  2732. } catch (BadLocationException e) {
  2733. // can't render I guess
  2734. //System.err.println("Can't render cursor");
  2735. }
  2736. }
  2737. }
  2738. //
  2739. // If some area other than the composed text is clicked by mouse,
  2740. // issue endComposition() to force commit the composed text.
  2741. //
  2742. protected void positionCaret(MouseEvent me) {
  2743. JTextComponent host = component;
  2744. Point pt = new Point(me.getX(), me.getY());
  2745. int offset = host.viewToModel(pt);
  2746. if ((offset < host.composedTextStart.getOffset()) ||
  2747. (offset > host.composedTextEnd.getOffset())) {
  2748. try {
  2749. // Issue endComposition
  2750. Position newPos = host.getDocument().createPosition(offset);
  2751. host.getInputContext().endComposition();
  2752. // Post a caret positioning runnable to assure that the positioning
  2753. // occurs *after* committing the composed text.
  2754. EventQueue.invokeLater(new DoSetCaretPosition(host, newPos));
  2755. } catch (BadLocationException ble) {
  2756. System.err.println(ble);
  2757. }
  2758. } else {
  2759. // Normal processing
  2760. super.positionCaret(me);
  2761. }
  2762. }
  2763. }
  2764. //
  2765. // Runnable class for invokeLater() to set caret position later.
  2766. //
  2767. private class DoSetCaretPosition implements Runnable {
  2768. JTextComponent host;
  2769. Position newPos;
  2770. DoSetCaretPosition(JTextComponent host, Position newPos) {
  2771. this.host = host;
  2772. this.newPos = newPos;
  2773. }
  2774. public void run() {
  2775. host.setCaretPosition(newPos.getOffset());
  2776. }
  2777. }
  2778. }