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