1. /*
  2. * @(#)BasicTextUI.java 1.44 00/04/06
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing.plaf.basic;
  11. import java.util.*;
  12. import java.awt.*;
  13. import java.awt.event.*;
  14. import java.awt.font.*;
  15. import java.beans.*;
  16. import java.io.*;
  17. import javax.swing.*;
  18. import javax.swing.plaf.*;
  19. import javax.swing.text.*;
  20. import javax.swing.event.*;
  21. import javax.swing.border.Border;
  22. /**
  23. * <p>
  24. * Basis of a text components look-and-feel. This provides the
  25. * basic editor view and controller services that may be useful
  26. * when creating a look-and-feel for an extension of JTextComponent.
  27. * <p>
  28. * Most state is held in the associated JTextComponent as bound
  29. * properties, and the UI installs default values for the
  30. * various properties. This default will install something for
  31. * all of the properties. Typically, a LAF implementation will
  32. * do more however. At a minimum, a LAF would generally install
  33. * key bindings.
  34. * <p>
  35. * This class also provides some concurrency support if the
  36. * Document associated with the JTextComponent is a subclass of
  37. * AbstractDocument. Access to the View (or View hierarchy) is
  38. * serialized between any thread mutating the model and the Swing
  39. * event thread (which is expected to render, do model/view coordinate
  40. * translation, etc). <em>Any access to the root view should first
  41. * aquire a read-lock on the AbstractDocument and release that lock
  42. * in a finally block.</em>
  43. * <p>
  44. * An important method to define is the {@link #getPropertyPrefix} method
  45. * which is used as the basis of the keys used to fetch defaults
  46. * from the UIManager. The string should reflect the type of
  47. * TextUI (eg. TextField, TextArea, etc) without the particular
  48. * LAF part of the name (eg Metal, Motif, etc).
  49. * <p>
  50. * To build a view of the model, one of the following strategies
  51. * can be employed.
  52. * <ol>
  53. * <li>
  54. * One strategy is to simply redefine the
  55. * ViewFactory interface in the UI. By default, this UI itself acts
  56. * as the factory for View implementations. This is useful
  57. * for simple factories. To do this reimplement the
  58. * {@link #create} method.
  59. * <li>
  60. * A common strategy for creating more complex types of documents
  61. * is to have the EditorKit implementation return a factory. Since
  62. * the EditorKit ties all of the pieces necessary to maintain a type
  63. * of document, the factory is typically an important part of that
  64. * and should be produced by the EditorKit implementation.
  65. * <li>
  66. * A less common way to create more complex types is to have
  67. * the UI implementation create a.
  68. * seperate object for the factory. To do this, the
  69. * {@link #createViewFactory} method should be reimplemented to
  70. * return some factory.
  71. * </ol>
  72. * <p>
  73. * <strong>Warning:</strong>
  74. * Serialized objects of this class will not be compatible with
  75. * future Swing releases. The current serialization support is appropriate
  76. * for short term storage or RMI between applications running the same
  77. * version of Swing. A future release of Swing will provide support for
  78. * long term persistence.
  79. *
  80. * @author Timothy Prinzing
  81. * @version 1.44 04/06/00
  82. */
  83. public abstract class BasicTextUI extends TextUI implements ViewFactory {
  84. /**
  85. * Creates a new UI.
  86. */
  87. public BasicTextUI() {
  88. painted = false;
  89. }
  90. /**
  91. * Creates the object to use for a caret. By default an
  92. * instance of BasicCaret is created. This method
  93. * can be redefined to provide something else that implements
  94. * the InputPosition interface or a subclass of JCaret.
  95. *
  96. * @return the caret object
  97. */
  98. protected Caret createCaret() {
  99. return new BasicCaret();
  100. }
  101. /**
  102. * Creates the object to use for adding highlights. By default
  103. * an instance of BasicHighlighter is created. This method
  104. * can be redefined to provide something else that implements
  105. * the Highlighter interface or a subclass of DefaultHighlighter.
  106. *
  107. * @return the highlighter
  108. */
  109. protected Highlighter createHighlighter() {
  110. return new BasicHighlighter();
  111. }
  112. /**
  113. * Fetches the name of the keymap that will be installed/used
  114. * by default for this UI. This is implemented to create a
  115. * name based upon the classname. The name is the the name
  116. * of the class with the package prefix removed.
  117. *
  118. * @return the name
  119. */
  120. protected String getKeymapName() {
  121. String nm = getClass().getName();
  122. int index = nm.lastIndexOf('.');
  123. if (index >= 0) {
  124. nm = nm.substring(index+1, nm.length());
  125. }
  126. return nm;
  127. }
  128. /**
  129. * Creates the keymap to use for the text component, and installs
  130. * any necessary bindings into it. By default, the keymap is
  131. * shared between all instances of this type of TextUI. The
  132. * keymap has the name defined by the getKeymapName method. If the
  133. * keymap is not found, then DEFAULT_KEYMAP from JTextComponent is used.
  134. * <p>
  135. * The set of bindings used to create the keymap is fetched
  136. * from the UIManager using a key formed by combining the
  137. * {@link #getPropertyPrefix} method
  138. * and the string <code>.keyBindings</code>. The type is expected
  139. * to be <code>JTextComponent.KeyBinding[]</code>.
  140. *
  141. * @return the keymap
  142. * @see #getKeymapName
  143. * @see javax.swing.text.JTextComponent
  144. */
  145. protected Keymap createKeymap() {
  146. String nm = getKeymapName();
  147. Keymap map = JTextComponent.getKeymap(nm);
  148. if (map == null) {
  149. Keymap parent = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
  150. map = JTextComponent.addKeymap(nm, parent);
  151. String prefix = getPropertyPrefix();
  152. Object o = UIManager.get(prefix + ".keyBindings");
  153. if ((o != null) && (o instanceof JTextComponent.KeyBinding[])) {
  154. JTextComponent.KeyBinding[] bindings = (JTextComponent.KeyBinding[]) o;
  155. JTextComponent.loadKeymap(map, bindings, getComponent().getActions());
  156. }
  157. }
  158. return map;
  159. }
  160. /**
  161. * This method gets called when a bound property is changed
  162. * on the associated JTextComponent. This is a hook
  163. * which UI implementations may change to reflect how the
  164. * UI displays bound properties of JTextComponent subclasses.
  165. * This is implemented to do nothing (i.e. the response to
  166. * properties in JTextComponent itself are handled prior
  167. * to calling this method).
  168. *
  169. * @param evt the property change event
  170. */
  171. protected void propertyChange(PropertyChangeEvent evt) {
  172. }
  173. /**
  174. * Gets the name used as a key to look up properties through the
  175. * UIManager. This is used as a prefix to all the standard
  176. * text properties.
  177. *
  178. * @return the name
  179. */
  180. protected abstract String getPropertyPrefix();
  181. /**
  182. * Initializes component properties, e.g. font, foreground,
  183. * background, caret color, selection color, selected text color,
  184. * disabled text color, and border color. The font, foreground, and
  185. * background properties are only set if their current value is either null
  186. * or a UIResource, other properties are set if the current
  187. * value is null.
  188. *
  189. * @see #uninstallDefaults
  190. * @see #installUI
  191. */
  192. protected void installDefaults()
  193. {
  194. String prefix = getPropertyPrefix();
  195. Font f = editor.getFont();
  196. if ((f == null) || (f instanceof UIResource)) {
  197. editor.setFont(UIManager.getFont(prefix + ".font"));
  198. }
  199. Color bg = editor.getBackground();
  200. if ((bg == null) || (bg instanceof UIResource)) {
  201. editor.setBackground(UIManager.getColor(prefix + ".background"));
  202. }
  203. Color fg = editor.getForeground();
  204. if ((fg == null) || (fg instanceof UIResource)) {
  205. editor.setForeground(UIManager.getColor(prefix + ".foreground"));
  206. }
  207. Color color = editor.getCaretColor();
  208. if ((color == null) || (color instanceof UIResource)) {
  209. editor.setCaretColor(UIManager.getColor(prefix + ".caretForeground"));
  210. }
  211. Color s = editor.getSelectionColor();
  212. if ((s == null) || (s instanceof UIResource)) {
  213. editor.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground"));
  214. }
  215. Color sfg = editor.getSelectedTextColor();
  216. if ((sfg == null) || (sfg instanceof UIResource)) {
  217. editor.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground"));
  218. }
  219. Color dfg = editor.getDisabledTextColor();
  220. if ((dfg == null) || (dfg instanceof UIResource)) {
  221. editor.setDisabledTextColor(UIManager.getColor(prefix + ".inactiveForeground"));
  222. }
  223. Border b = editor.getBorder();
  224. if ((b == null) || (b instanceof UIResource)) {
  225. editor.setBorder(UIManager.getBorder(prefix + ".border"));
  226. }
  227. Insets margin = editor.getMargin();
  228. if (margin == null || margin instanceof UIResource) {
  229. editor.setMargin(UIManager.getInsets(prefix + ".margin"));
  230. }
  231. Caret caret = editor.getCaret();
  232. if (caret == null || caret instanceof UIResource) {
  233. caret = createCaret();
  234. editor.setCaret(caret);
  235. Object o = UIManager.get(prefix + ".caretBlinkRate");
  236. if ((o != null) && (o instanceof Integer)) {
  237. Integer rate = (Integer) o;
  238. caret.setBlinkRate(rate.intValue());
  239. }
  240. }
  241. Highlighter highlighter = editor.getHighlighter();
  242. if (highlighter == null || highlighter instanceof UIResource) {
  243. editor.setHighlighter(createHighlighter());
  244. }
  245. }
  246. /**
  247. * Sets the component properties that haven't been explicitly overriden to
  248. * null. A property is considered overridden if its current value
  249. * is not a UIResource.
  250. *
  251. * @see #installDefaults
  252. * @see #uninstallUI
  253. */
  254. protected void uninstallDefaults()
  255. {
  256. if (editor.getCaretColor() instanceof UIResource) {
  257. editor.setCaretColor(null);
  258. }
  259. if (editor.getSelectionColor() instanceof UIResource) {
  260. editor.setSelectionColor(null);
  261. }
  262. if (editor.getDisabledTextColor() instanceof UIResource) {
  263. editor.setDisabledTextColor(null);
  264. }
  265. if (editor.getSelectedTextColor() instanceof UIResource) {
  266. editor.setSelectedTextColor(null);
  267. }
  268. if (editor.getBorder() instanceof UIResource) {
  269. editor.setBorder(null);
  270. }
  271. if (editor.getMargin() instanceof UIResource) {
  272. editor.setMargin(null);
  273. }
  274. if (editor.getCaret() instanceof UIResource) {
  275. editor.setCaret(null);
  276. }
  277. if (editor.getHighlighter() instanceof UIResource) {
  278. editor.setHighlighter(null);
  279. }
  280. }
  281. /**
  282. * Installs listeners for the UI.
  283. */
  284. protected void installListeners() {
  285. }
  286. /**
  287. * Uninstalls listeners for the UI.
  288. */
  289. protected void uninstallListeners() {
  290. }
  291. protected void installKeyboardActions() {
  292. // backward compatibility support... keymaps for the UI
  293. // are now installed in the more friendly input map.
  294. editor.setKeymap(createKeymap());
  295. InputMap km = getInputMap();
  296. if (km != null) {
  297. SwingUtilities.replaceUIInputMap(editor, JComponent.WHEN_FOCUSED,
  298. km);
  299. }
  300. ActionMap map = getActionMap();
  301. if (map != null) {
  302. SwingUtilities.replaceUIActionMap(editor, map);
  303. }
  304. updateFocusAcceleratorBinding(false);
  305. }
  306. /**
  307. * Get the InputMap to use for the UI.
  308. */
  309. InputMap getInputMap() {
  310. InputMap map = new InputMapUIResource();
  311. InputMap shared =
  312. (InputMap)UIManager.get(getPropertyPrefix() + ".focusInputMap");
  313. if (shared != null) {
  314. map.setParent(shared);
  315. }
  316. return map;
  317. }
  318. /**
  319. * Invoked when the focus accelerator changes, this will update the
  320. * key bindings as necessary.
  321. */
  322. void updateFocusAcceleratorBinding(boolean changed) {
  323. char accelerator = editor.getFocusAccelerator();
  324. if (changed || accelerator != '\0') {
  325. InputMap km = SwingUtilities.getUIInputMap
  326. (editor, JComponent.WHEN_IN_FOCUSED_WINDOW);
  327. if (km == null && accelerator != '\0') {
  328. km = new ComponentInputMapUIResource(editor);
  329. SwingUtilities.replaceUIInputMap(editor, JComponent.
  330. WHEN_IN_FOCUSED_WINDOW, km);
  331. ActionMap am = getActionMap();
  332. SwingUtilities.replaceUIActionMap(editor, am);
  333. }
  334. if (km != null) {
  335. km.clear();
  336. if (accelerator != '\0') {
  337. km.put(KeyStroke.getKeyStroke(accelerator,
  338. ActionEvent.ALT_MASK),
  339. "requestFocus");
  340. }
  341. }
  342. }
  343. }
  344. /**
  345. * Fetch an action map to use. This map is shared across
  346. * TextUI implementations of a given type.
  347. */
  348. ActionMap getActionMap() {
  349. String mapName = getPropertyPrefix() + ".actionMap";
  350. ActionMap map = (ActionMap)UIManager.get(mapName);
  351. if (map == null) {
  352. map = createActionMap();
  353. if (map != null) {
  354. UIManager.put(mapName, map);
  355. }
  356. }
  357. ActionMap componentMap = new ActionMapUIResource();
  358. componentMap.put("requestFocus", new FocusAction());
  359. if (map != null) {
  360. componentMap.setParent(map);
  361. }
  362. return componentMap;
  363. }
  364. /**
  365. * Create a default action map. This is basically the
  366. * set of actions found exported by the component.
  367. */
  368. ActionMap createActionMap() {
  369. ActionMap map = new ActionMapUIResource();
  370. Action[] actions = editor.getActions();
  371. //System.out.println("building map for UI: " + getPropertyPrefix());
  372. int n = actions.length;
  373. for (int i = 0; i < n; i++) {
  374. Action a = actions[i];
  375. map.put(a.getValue(Action.NAME), a);
  376. //System.out.println(" " + a.getValue(Action.NAME));
  377. }
  378. return map;
  379. }
  380. protected void uninstallKeyboardActions() {
  381. editor.setKeymap(null);
  382. SwingUtilities.replaceUIInputMap(editor, JComponent.
  383. WHEN_IN_FOCUSED_WINDOW, null);
  384. SwingUtilities.replaceUIActionMap(editor, null);
  385. }
  386. /**
  387. * Paints a background for the view. This will only be
  388. * called if isOpaque() on the associated component is
  389. * true. The default is to paint the background color
  390. * of the component.
  391. *
  392. * @param g the graphics context
  393. */
  394. protected void paintBackground(Graphics g) {
  395. g.setColor(editor.getBackground());
  396. g.fillRect(0, 0, editor.getWidth(), editor.getHeight());
  397. }
  398. /**
  399. * Fetches the text component associated with this
  400. * UI implementation. This will be null until
  401. * the ui has been installed.
  402. *
  403. * @return the editor component
  404. */
  405. protected final JTextComponent getComponent() {
  406. return editor;
  407. }
  408. /**
  409. * Flags model changes.
  410. * This is called whenever the model has changed.
  411. * It is implemented to rebuild the view hierarchy
  412. * to represent the default root element of the
  413. * associated model.
  414. */
  415. protected void modelChanged() {
  416. // create a view hierarchy
  417. ViewFactory f = rootView.getViewFactory();
  418. Document doc = editor.getDocument();
  419. Element elem = doc.getDefaultRootElement();
  420. setView(f.create(elem));
  421. }
  422. /**
  423. * Sets the current root of the view hierarchy and calls invalidate().
  424. * If there were any child components, they will be removed (i.e.
  425. * there are assumed to have come from components embedded in views).
  426. *
  427. * @param v the root view
  428. */
  429. protected final void setView(View v) {
  430. editor.removeAll();
  431. rootView.setView(v);
  432. painted = false;
  433. editor.revalidate();
  434. editor.repaint();
  435. }
  436. /**
  437. * Paints the interface safely with a guarantee that
  438. * the model won't change from the view of this thread.
  439. * This does the following things, rendering from
  440. * back to front.
  441. * <ol>
  442. * <li>
  443. * If the component is marked as opaque, the background
  444. * is painted in the current background color of the
  445. * component.
  446. * <li>
  447. * The highlights (if any) are painted.
  448. * <li>
  449. * The view hierarchy is painted.
  450. * <li>
  451. * The caret is painted.
  452. * </ol>
  453. *
  454. * @param g the graphics context
  455. */
  456. protected void paintSafely(Graphics g) {
  457. painted = true;
  458. Highlighter highlighter = editor.getHighlighter();
  459. Caret caret = editor.getCaret();
  460. // paint the background
  461. if (editor.isOpaque()) {
  462. paintBackground(g);
  463. }
  464. // paint the highlights
  465. if (highlighter != null) {
  466. highlighter.paint(g);
  467. }
  468. // paint the view hierarchy
  469. Rectangle alloc = getVisibleEditorRect();
  470. rootView.paint(g, alloc);
  471. // paint the caret
  472. if (caret != null) {
  473. caret.paint(g);
  474. }
  475. }
  476. // --- ComponentUI methods --------------------------------------------
  477. /**
  478. * Installs the UI for a component. This does the following
  479. * things.
  480. * <ol>
  481. * <li>
  482. * Set the associated component to opaque (can be changed
  483. * easily by a subclass or on JTextComponent directly),
  484. * which is the most common case. This will cause the
  485. * component's background color to be painted.
  486. * <li>
  487. * Install the default caret and highlighter into the
  488. * associated component.
  489. * <li>
  490. * Attach to the editor and model. If there is no
  491. * model, a default one is created.
  492. * <li>
  493. * create the view factory and the view hierarchy used
  494. * to represent the model.
  495. * </ol>
  496. *
  497. * @param c the editor component
  498. * @see ComponentUI#installUI
  499. */
  500. public void installUI(JComponent c) {
  501. if (c instanceof JTextComponent) {
  502. editor = (JTextComponent) c;
  503. // install defaults
  504. installDefaults();
  505. // common case is background painted... this can
  506. // easily be changed by subclasses or from outside
  507. // of the component.
  508. editor.setOpaque(true);
  509. editor.setAutoscrolls(true);
  510. // attach to the model and editor
  511. editor.addPropertyChangeListener(updateHandler);
  512. Document doc = editor.getDocument();
  513. if (doc == null) {
  514. // no model, create a default one. This will
  515. // fire a notification to the updateHandler
  516. // which takes care of the rest.
  517. editor.setDocument(getEditorKit(editor).createDefaultDocument());
  518. } else {
  519. doc.addDocumentListener(updateHandler);
  520. modelChanged();
  521. }
  522. // install keymap
  523. installListeners();
  524. installKeyboardActions();
  525. LayoutManager oldLayout = editor.getLayout();
  526. if ((oldLayout == null) || (oldLayout instanceof UIResource)) {
  527. // by default, use default LayoutManger implementation that
  528. // will position the components associated with a View object.
  529. editor.setLayout(updateHandler);
  530. }
  531. } else {
  532. throw new Error("TextUI needs JTextComponent");
  533. }
  534. }
  535. /**
  536. * Deinstalls the UI for a component. This removes the listeners,
  537. * uninstalls the highlighter, removes views, and nulls out the keymap.
  538. *
  539. * @param c the editor component
  540. * @see ComponentUI#uninstallUI
  541. */
  542. public void uninstallUI(JComponent c) {
  543. // detach from the model
  544. editor.removePropertyChangeListener(updateHandler);
  545. editor.getDocument().removeDocumentListener(updateHandler);
  546. // view part
  547. painted = false;
  548. uninstallDefaults();
  549. rootView.setView(null);
  550. c.removeAll();
  551. LayoutManager lm = c.getLayout();
  552. if (lm instanceof UIResource) {
  553. c.setLayout(null);
  554. }
  555. // controller part
  556. uninstallKeyboardActions();
  557. uninstallListeners();
  558. }
  559. /**
  560. * Superclass paints background in an uncontrollable way
  561. * (i.e. one might want an image tiled into the background).
  562. * To prevent this from happening twice, this method is
  563. * reimplemented to simply paint.
  564. * <p>
  565. * <em>NOTE:</em> Superclass is also not thread-safe in
  566. * it's rendering of the background, although that's not
  567. * an issue with the default rendering.
  568. */
  569. public void update(Graphics g, JComponent c) {
  570. paint(g, c);
  571. }
  572. /**
  573. * Paints the interface. This is routed to the
  574. * paintSafely method under the guarantee that
  575. * the model won't change from the view of this thread
  576. * while it's rendering (if the associated model is
  577. * derived from AbstractDocument). This enables the
  578. * model to potentially be updated asynchronously.
  579. *
  580. * @param g the graphics context
  581. * @param c the editor component
  582. */
  583. public final void paint(Graphics g, JComponent c) {
  584. if ((rootView.getViewCount() > 0) && (rootView.getView(0) != null)) {
  585. Document doc = editor.getDocument();
  586. if (doc instanceof AbstractDocument) {
  587. ((AbstractDocument)doc).readLock();
  588. }
  589. try {
  590. paintSafely(g);
  591. } finally {
  592. if (doc instanceof AbstractDocument) {
  593. ((AbstractDocument)doc).readUnlock();
  594. }
  595. }
  596. }
  597. }
  598. /**
  599. * Gets the preferred size for the editor component. If the component
  600. * has been given a size prior to receiving this request, it will
  601. * set the size of the view hierarchy to reflect the size of the component
  602. * before requesting the preferred size of the view hierarchy. This
  603. * allows formatted views to format to the current component size before
  604. * answering the request. Other views don't care about currently formatted
  605. * size and give the same answer either way.
  606. *
  607. * @param c the editor component
  608. * @return the size
  609. */
  610. public Dimension getPreferredSize(JComponent c) {
  611. Document doc = editor.getDocument();
  612. Insets i = c.getInsets();
  613. Dimension d = c.getSize();
  614. if (doc instanceof AbstractDocument) {
  615. ((AbstractDocument)doc).readLock();
  616. }
  617. try {
  618. if ((d.width > (i.left + i.right)) && (d.height > (i.top + i.bottom))) {
  619. rootView.setSize(d.width - i.left - i.right, d.height - i.top - i.bottom);
  620. }
  621. d.width = (int) Math.min((long) rootView.getPreferredSpan(View.X_AXIS) +
  622. (long) i.left + (long) i.right, Integer.MAX_VALUE);
  623. d.height = (int) Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) +
  624. (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
  625. } finally {
  626. if (doc instanceof AbstractDocument) {
  627. ((AbstractDocument)doc).readUnlock();
  628. }
  629. }
  630. return d;
  631. }
  632. /**
  633. * Gets the minimum size for the editor component.
  634. *
  635. * @param c the editor component
  636. * @return the size
  637. */
  638. public Dimension getMinimumSize(JComponent c) {
  639. Document doc = editor.getDocument();
  640. Insets i = c.getInsets();
  641. Dimension d = new Dimension();
  642. if (doc instanceof AbstractDocument) {
  643. ((AbstractDocument)doc).readLock();
  644. }
  645. try {
  646. d.width = (int) rootView.getMinimumSpan(View.X_AXIS) + i.left + i.right;
  647. d.height = (int) rootView.getMinimumSpan(View.Y_AXIS) + i.top + i.bottom;
  648. } finally {
  649. if (doc instanceof AbstractDocument) {
  650. ((AbstractDocument)doc).readUnlock();
  651. }
  652. }
  653. return d;
  654. }
  655. /**
  656. * Gets the maximum size for the editor component.
  657. *
  658. * @param c the editor component
  659. * @return the size
  660. */
  661. public Dimension getMaximumSize(JComponent c) {
  662. Document doc = editor.getDocument();
  663. Insets i = c.getInsets();
  664. Dimension d = new Dimension();
  665. if (doc instanceof AbstractDocument) {
  666. ((AbstractDocument)doc).readLock();
  667. }
  668. try {
  669. d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS) +
  670. (long) i.left + (long) i.right, Integer.MAX_VALUE);
  671. d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS) +
  672. (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
  673. } finally {
  674. if (doc instanceof AbstractDocument) {
  675. ((AbstractDocument)doc).readUnlock();
  676. }
  677. }
  678. return d;
  679. }
  680. // ---- TextUI methods -------------------------------------------
  681. /**
  682. * Gets the allocation to give the root View. Due
  683. * to an unfortunate set of historical events this
  684. * method is inappropriately named. The Rectangle
  685. * returned has nothing to do with visibility.
  686. * The component must have a non-zero positive size for
  687. * this translation to be computed.
  688. *
  689. * @return the bounding box for the root view
  690. */
  691. protected Rectangle getVisibleEditorRect() {
  692. Rectangle alloc = editor.getBounds();
  693. if ((alloc.width > 0) && (alloc.height > 0)) {
  694. alloc.x = alloc.y = 0;
  695. Insets insets = editor.getInsets();
  696. alloc.x += insets.left;
  697. alloc.y += insets.top;
  698. alloc.width -= insets.left + insets.right;
  699. alloc.height -= insets.top + insets.bottom;
  700. return alloc;
  701. }
  702. return null;
  703. }
  704. /**
  705. * Converts the given location in the model to a place in
  706. * the view coordinate system.
  707. * The component must have a non-zero positive size for
  708. * this translation to be computed.
  709. *
  710. * @param tc the text component for which this UI is installed
  711. * @param pos the local location in the model to translate >= 0
  712. * @return the coordinates as a rectangle, null if the model is not painted
  713. * @exception BadLocationException if the given position does not
  714. * represent a valid location in the associated document
  715. * @see TextUI#modelToView
  716. */
  717. public Rectangle modelToView(JTextComponent tc, int pos) throws BadLocationException {
  718. return modelToView(tc, pos, Position.Bias.Forward);
  719. }
  720. /**
  721. * Converts the given location in the model to a place in
  722. * the view coordinate system.
  723. * The component must have a non-zero positive size for
  724. * this translation to be computed.
  725. *
  726. * @param tc the text component for which this UI is installed
  727. * @param pos the local location in the model to translate >= 0
  728. * @return the coordinates as a rectangle, null if the model is not painted
  729. * @exception BadLocationException if the given position does not
  730. * represent a valid location in the associated document
  731. * @see TextUI#modelToView
  732. */
  733. public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias) throws BadLocationException {
  734. Document doc = editor.getDocument();
  735. if (doc instanceof AbstractDocument) {
  736. ((AbstractDocument)doc).readLock();
  737. }
  738. try {
  739. Rectangle alloc = getVisibleEditorRect();
  740. if (alloc != null) {
  741. rootView.setSize(alloc.width, alloc.height);
  742. Shape s = rootView.modelToView(pos, alloc, bias);
  743. if (s != null) {
  744. return s.getBounds();
  745. }
  746. }
  747. } finally {
  748. if (doc instanceof AbstractDocument) {
  749. ((AbstractDocument)doc).readUnlock();
  750. }
  751. }
  752. return null;
  753. }
  754. /**
  755. * Converts the given place in the view coordinate system
  756. * to the nearest representative location in the model.
  757. * The component must have a non-zero positive size for
  758. * this translation to be computed.
  759. *
  760. * @param tc the text component for which this UI is installed
  761. * @param pt the location in the view to translate. This
  762. * should be in the same coordinate system as the mouse events.
  763. * @return the offset from the start of the document >= 0,
  764. * -1 if not painted
  765. * @see TextUI#viewToModel
  766. */
  767. public int viewToModel(JTextComponent tc, Point pt) {
  768. return viewToModel(tc, pt, discardBias);
  769. }
  770. /**
  771. * Converts the given place in the view coordinate system
  772. * to the nearest representative location in the model.
  773. * The component must have a non-zero positive size for
  774. * this translation to be computed.
  775. *
  776. * @param tc the text component for which this UI is installed
  777. * @param pt the location in the view to translate. This
  778. * should be in the same coordinate system as the mouse events.
  779. * @return the offset from the start of the document >= 0,
  780. * -1 if the component doesn't yet have a positive size.
  781. * @see TextUI#viewToModel
  782. */
  783. public int viewToModel(JTextComponent tc, Point pt,
  784. Position.Bias[] biasReturn) {
  785. int offs = -1;
  786. Document doc = editor.getDocument();
  787. if (doc instanceof AbstractDocument) {
  788. ((AbstractDocument)doc).readLock();
  789. }
  790. try {
  791. Rectangle alloc = getVisibleEditorRect();
  792. if (alloc != null) {
  793. rootView.setSize(alloc.width, alloc.height);
  794. offs = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);
  795. }
  796. } finally {
  797. if (doc instanceof AbstractDocument) {
  798. ((AbstractDocument)doc).readUnlock();
  799. }
  800. }
  801. return offs;
  802. }
  803. /**
  804. * Provides a way to determine the next visually represented model
  805. * location that one might place a caret. Some views may not be visible,
  806. * they might not be in the same order found in the model, or they just
  807. * might not allow access to some of the locations in the model.
  808. *
  809. * @param pos the position to convert >= 0
  810. * @param a the allocated region to render into
  811. * @param direction the direction from the current position that can
  812. * be thought of as the arrow keys typically found on a keyboard.
  813. * This may be SwingConstants.WEST, SwingConstants.EAST,
  814. * SwingConstants.NORTH, or SwingConstants.SOUTH.
  815. * @return the location within the model that best represents the next
  816. * location visual position.
  817. * @exception BadLocationException
  818. * @exception IllegalArgumentException for an invalid direction
  819. */
  820. public int getNextVisualPositionFrom(JTextComponent t, int pos,
  821. Position.Bias b, int direction, Position.Bias[] biasRet)
  822. throws BadLocationException{
  823. Document doc = editor.getDocument();
  824. if (doc instanceof AbstractDocument) {
  825. ((AbstractDocument)doc).readLock();
  826. }
  827. try {
  828. if (painted) {
  829. Rectangle alloc = getVisibleEditorRect();
  830. rootView.setSize(alloc.width, alloc.height);
  831. return rootView.getNextVisualPositionFrom(pos, b, alloc, direction,
  832. biasRet);
  833. }
  834. } finally {
  835. if (doc instanceof AbstractDocument) {
  836. ((AbstractDocument)doc).readUnlock();
  837. }
  838. }
  839. return -1;
  840. }
  841. /**
  842. * Causes the portion of the view responsible for the
  843. * given part of the model to be repainted. Does nothing if
  844. * the view is not currently painted.
  845. *
  846. * @param tc the text component for which this UI is installed
  847. * @param p0 the beginning of the range >= 0
  848. * @param p1 the end of the range >= p0
  849. * @see TextUI#damageRange
  850. */
  851. public void damageRange(JTextComponent tc, int p0, int p1) {
  852. damageRange(tc, p0, p1, Position.Bias.Forward, Position.Bias.Backward);
  853. }
  854. /**
  855. * Causes the portion of the view responsible for the
  856. * given part of the model to be repainted.
  857. *
  858. * @param p0 the beginning of the range >= 0
  859. * @param p1 the end of the range >= p0
  860. */
  861. public void damageRange(JTextComponent t, int p0, int p1,
  862. Position.Bias p0Bias, Position.Bias p1Bias) {
  863. if (painted) {
  864. Rectangle alloc = getVisibleEditorRect();
  865. Document doc = t.getDocument();
  866. if (doc instanceof AbstractDocument) {
  867. ((AbstractDocument)doc).readLock();
  868. }
  869. try {
  870. rootView.setSize(alloc.width, alloc.height);
  871. Shape toDamage = rootView.modelToView(p0, p0Bias,
  872. p1, p1Bias, alloc);
  873. Rectangle rect = (toDamage instanceof Rectangle) ?
  874. (Rectangle)toDamage : toDamage.getBounds();
  875. editor.repaint(rect.x, rect.y, rect.width, rect.height);
  876. } catch (BadLocationException e) {
  877. } finally {
  878. if (doc instanceof AbstractDocument) {
  879. ((AbstractDocument)doc).readUnlock();
  880. }
  881. }
  882. }
  883. }
  884. /**
  885. * Fetches the EditorKit for the UI.
  886. *
  887. * @param tc the text component for which this UI is installed
  888. * @return the editor capabilities
  889. * @see TextUI#getEditorKit
  890. */
  891. public EditorKit getEditorKit(JTextComponent tc) {
  892. return defaultKit;
  893. }
  894. /**
  895. * Fetches a View with the allocation of the associated
  896. * text component (i.e. the root of the hierarchy) that
  897. * can be traversed to determine how the model is being
  898. * represented spatially.
  899. * <p>
  900. * <font color=red><b>NOTE:</b>The View hierarchy can
  901. * be traversed from the root view, and other things
  902. * can be done as well. Things done in this way cannot
  903. * be protected like simple method calls through the TextUI.
  904. * Therefore, proper operation in the presence of concurrency
  905. * must be arranged by any logic that calls this method!
  906. * </font>
  907. *
  908. * @param tc the text component for which this UI is installed
  909. * @return the view
  910. * @see TextUI#getRootView
  911. */
  912. public View getRootView(JTextComponent tc) {
  913. return rootView;
  914. }
  915. // --- ViewFactory methods ------------------------------
  916. /**
  917. * Creates a view for an element.
  918. * If a subclass wishes to directly implement the factory
  919. * producing the view(s), it should reimplement this
  920. * method. By default it simply returns null indicating
  921. * it is unable to represent the element.
  922. *
  923. * @param elem the element
  924. * @return the view
  925. */
  926. public View create(Element elem) {
  927. return null;
  928. }
  929. /**
  930. * Creates a view for an element.
  931. * If a subclass wishes to directly implement the factory
  932. * producing the view(s), it should reimplement this
  933. * method. By default it simply returns null indicating
  934. * it is unable to represent the part of the element.
  935. *
  936. * @param elem the element
  937. * @param p0 the starting offset >= 0
  938. * @param p1 the ending offset >= p0
  939. * @return the view
  940. */
  941. public View create(Element elem, int p0, int p1) {
  942. return null;
  943. }
  944. public static class BasicCaret extends DefaultCaret implements UIResource {}
  945. public static class BasicHighlighter extends DefaultHighlighter implements UIResource {}
  946. // ----- member variables ---------------------------------------
  947. private static final EditorKit defaultKit = new DefaultEditorKit();
  948. transient JTextComponent editor;
  949. transient boolean painted;
  950. transient RootView rootView = new RootView();
  951. transient UpdateHandler updateHandler = new UpdateHandler();
  952. private static final Position.Bias[] discardBias = new Position.Bias[1];
  953. /**
  954. * Root view that acts as a gateway between the component
  955. * and the View hierarchy.
  956. */
  957. class RootView extends View {
  958. RootView() {
  959. super(null);
  960. }
  961. void setView(View v) {
  962. if (view != null) {
  963. // get rid of back reference so that the old
  964. // hierarchy can be garbage collected.
  965. view.setParent(null);
  966. }
  967. view = v;
  968. if (view != null) {
  969. view.setParent(this);
  970. }
  971. }
  972. /**
  973. * Fetches the attributes to use when rendering. At the root
  974. * level there are no attributes. If an attribute is resolved
  975. * up the view hierarchy this is the end of the line.
  976. */
  977. public AttributeSet getAttributes() {
  978. return null;
  979. }
  980. /**
  981. * Determines the preferred span for this view along an axis.
  982. *
  983. * @param axis may be either X_AXIS or Y_AXIS
  984. * @return the span the view would like to be rendered into.
  985. * Typically the view is told to render into the span
  986. * that is returned, although there is no guarantee.
  987. * The parent may choose to resize or break the view.
  988. */
  989. public float getPreferredSpan(int axis) {
  990. if (view != null) {
  991. return view.getPreferredSpan(axis);
  992. }
  993. return 10;
  994. }
  995. /**
  996. * Determines the minimum span for this view along an axis.
  997. *
  998. * @param axis may be either X_AXIS or Y_AXIS
  999. * @return the span the view would like to be rendered into.
  1000. * Typically the view is told to render into the span
  1001. * that is returned, although there is no guarantee.
  1002. * The parent may choose to resize or break the view.
  1003. */
  1004. public float getMinimumSpan(int axis) {
  1005. if (view != null) {
  1006. return view.getMinimumSpan(axis);
  1007. }
  1008. return 10;
  1009. }
  1010. /**
  1011. * Determines the maximum span for this view along an axis.
  1012. *
  1013. * @param axis may be either X_AXIS or Y_AXIS
  1014. * @return the span the view would like to be rendered into.
  1015. * Typically the view is told to render into the span
  1016. * that is returned, although there is no guarantee.
  1017. * The parent may choose to resize or break the view.
  1018. */
  1019. public float getMaximumSpan(int axis) {
  1020. return Integer.MAX_VALUE;
  1021. }
  1022. /**
  1023. * Specifies that a preference has changed.
  1024. * Child views can call this on the parent to indicate that
  1025. * the preference has changed. The root view routes this to
  1026. * invalidate on the hosting component.
  1027. * <p>
  1028. * This can be called on a different thread from the
  1029. * event dispatching thread and is basically unsafe to
  1030. * propagate into the component. To make this safe,
  1031. * the operation is transferred over to the event dispatching
  1032. * thread for completion. It is a design goal that all view
  1033. * methods be safe to call without concern for concurrency,
  1034. * and this behavior helps make that true.
  1035. *
  1036. * @param child the child view
  1037. * @param width true if the width preference has changed
  1038. * @param height true if the height preference has changed
  1039. */
  1040. public void preferenceChanged(View child, boolean width, boolean height) {
  1041. editor.revalidate();
  1042. }
  1043. /**
  1044. * Determines the desired alignment for this view along an axis.
  1045. *
  1046. * @param axis may be either X_AXIS or Y_AXIS
  1047. * @return the desired alignment, where 0.0 indicates the origin
  1048. * and 1.0 the full span away from the origin
  1049. */
  1050. public float getAlignment(int axis) {
  1051. if (view != null) {
  1052. return view.getAlignment(axis);
  1053. }
  1054. return 0;
  1055. }
  1056. /**
  1057. * Renders the view.
  1058. *
  1059. * @param g the graphics context
  1060. * @param allocation the region to render into
  1061. */
  1062. public void paint(Graphics g, Shape allocation) {
  1063. if (view != null) {
  1064. Rectangle alloc = (allocation instanceof Rectangle) ?
  1065. (Rectangle)allocation : allocation.getBounds();
  1066. view.setSize(alloc.width, alloc.height);
  1067. view.paint(g, allocation);
  1068. }
  1069. }
  1070. /**
  1071. * Sets the view parent.
  1072. *
  1073. * @param parent the parent view
  1074. */
  1075. public void setParent(View parent) {
  1076. throw new Error("Can't set parent on root view");
  1077. }
  1078. /**
  1079. * Returns the number of views in this view. Since
  1080. * this view simply wraps the root of the view hierarchy
  1081. * it has exactly one child.
  1082. *
  1083. * @return the number of views
  1084. * @see #getView
  1085. */
  1086. public int getViewCount() {
  1087. return 1;
  1088. }
  1089. /**
  1090. * Gets the n-th view in this container.
  1091. *
  1092. * @param n the number of the view to get
  1093. * @return the view
  1094. */
  1095. public View getView(int n) {
  1096. return view;
  1097. }
  1098. /**
  1099. * Returns the child view index representing the given position in
  1100. * the model. This is implemented to return the index of the only
  1101. * child.
  1102. *
  1103. * @param pos the position >= 0
  1104. * @returns index of the view representing the given position, or
  1105. * -1 if no view represents that position
  1106. * @since 1.3
  1107. */
  1108. public int getViewIndex(int pos, Position.Bias b) {
  1109. return 0;
  1110. }
  1111. /**
  1112. * Fetches the allocation for the given child view.
  1113. * This enables finding out where various views
  1114. * are located, without assuming the views store
  1115. * their location. This returns the given allocation
  1116. * since this view simply acts as a gateway between
  1117. * the view hierarchy and the associated component.
  1118. *
  1119. * @param index the index of the child
  1120. * @param a the allocation to this view.
  1121. * @return the allocation to the child
  1122. */
  1123. public Shape getChildAllocation(int index, Shape a) {
  1124. return a;
  1125. }
  1126. /**
  1127. * Provides a mapping from the document model coordinate space
  1128. * to the coordinate space of the view mapped to it.
  1129. *
  1130. * @param pos the position to convert
  1131. * @param a the allocated region to render into
  1132. * @return the bounding box of the given position
  1133. */
  1134. public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  1135. if (view != null) {
  1136. return view.modelToView(pos, a, b);
  1137. }
  1138. return null;
  1139. }
  1140. /**
  1141. * Provides a mapping from the document model coordinate space
  1142. * to the coordinate space of the view mapped to it.
  1143. *
  1144. * @param p0 the position to convert >= 0
  1145. * @param b0 the bias toward the previous character or the
  1146. * next character represented by p0, in case the
  1147. * position is a boundary of two views.
  1148. * @param p1 the position to convert >= 0
  1149. * @param b1 the bias toward the previous character or the
  1150. * next character represented by p1, in case the
  1151. * position is a boundary of two views.
  1152. * @param a the allocated region to render into
  1153. * @return the bounding box of the given position is returned
  1154. * @exception BadLocationException if the given position does
  1155. * not represent a valid location in the associated document
  1156. * @exception IllegalArgumentException for an invalid bias argument
  1157. * @see View#viewToModel
  1158. */
  1159. public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a) throws BadLocationException {
  1160. if (view != null) {
  1161. return view.modelToView(p0, b0, p1, b1, a);
  1162. }
  1163. return null;
  1164. }
  1165. /**
  1166. * Provides a mapping from the view coordinate space to the logical
  1167. * coordinate space of the model.
  1168. *
  1169. * @param x x coordinate of the view location to convert
  1170. * @param y y coordinate of the view location to convert
  1171. * @param a the allocated region to render into
  1172. * @return the location within the model that best represents the
  1173. * given point in the view
  1174. */
  1175. public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
  1176. if (view != null) {
  1177. int retValue = view.viewToModel(x, y, a, bias);
  1178. return retValue;
  1179. }
  1180. return -1;
  1181. }
  1182. /**
  1183. * Provides a way to determine the next visually represented model
  1184. * location that one might place a caret. Some views may not be visible,
  1185. * they might not be in the same order found in the model, or they just
  1186. * might not allow access to some of the locations in the model.
  1187. *
  1188. * @param pos the position to convert >= 0
  1189. * @param a the allocated region to render into
  1190. * @param direction the direction from the current position that can
  1191. * be thought of as the arrow keys typically found on a keyboard.
  1192. * This may be SwingConstants.WEST, SwingConstants.EAST,
  1193. * SwingConstants.NORTH, or SwingConstants.SOUTH.
  1194. * @return the location within the model that best represents the next
  1195. * location visual position.
  1196. * @exception BadLocationException
  1197. * @exception IllegalArgumentException for an invalid direction
  1198. */
  1199. public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
  1200. int direction,
  1201. Position.Bias[] biasRet)
  1202. throws BadLocationException {
  1203. if( view != null ) {
  1204. int nextPos = view.getNextVisualPositionFrom(pos, b, a,
  1205. direction, biasRet);
  1206. if(nextPos != -1) {
  1207. pos = nextPos;
  1208. }
  1209. else {
  1210. biasRet[0] = b;
  1211. }
  1212. }
  1213. return pos;
  1214. }
  1215. /**
  1216. * Gives notification that something was inserted into the document
  1217. * in a location that this view is responsible for.
  1218. *
  1219. * @param e the change information from the associated document
  1220. * @param a the current allocation of the view
  1221. * @param f the factory to use to rebuild if the view has children
  1222. */
  1223. public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  1224. if (view != null) {
  1225. view.insertUpdate(e, a, f);
  1226. }
  1227. }
  1228. /**
  1229. * Gives notification that something was removed from the document
  1230. * in a location that this view is responsible for.
  1231. *
  1232. * @param e the change information from the associated document
  1233. * @param a the current allocation of the view
  1234. * @param f the factory to use to rebuild if the view has children
  1235. */
  1236. public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  1237. if (view != null) {
  1238. view.removeUpdate(e, a, f);
  1239. }
  1240. }
  1241. /**
  1242. * Gives notification from the document that attributes were changed
  1243. * in a location that this view is responsible for.
  1244. *
  1245. * @param e the change information from the associated document
  1246. * @param a the current allocation of the view
  1247. * @param f the factory to use to rebuild if the view has children
  1248. */
  1249. public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  1250. if (view != null) {
  1251. view.changedUpdate(e, a, f);
  1252. }
  1253. }
  1254. /**
  1255. * Returns the document model underlying the view.
  1256. *
  1257. * @return the model
  1258. */
  1259. public Document getDocument() {
  1260. return editor.getDocument();
  1261. }
  1262. /**
  1263. * Returns the starting offset into the model for this view.
  1264. *
  1265. * @return the starting offset
  1266. */
  1267. public int getStartOffset() {
  1268. if (view != null) {
  1269. return view.getStartOffset();
  1270. }
  1271. return getElement().getStartOffset();
  1272. }
  1273. /**
  1274. * Returns the ending offset into the model for this view.
  1275. *
  1276. * @return the ending offset
  1277. */
  1278. public int getEndOffset() {
  1279. if (view != null) {
  1280. return view.getEndOffset();
  1281. }
  1282. return getElement().getEndOffset();
  1283. }
  1284. /**
  1285. * Gets the element that this view is mapped to.
  1286. *
  1287. * @return the view
  1288. */
  1289. public Element getElement() {
  1290. if (view != null) {
  1291. return view.getElement();
  1292. }
  1293. return editor.getDocument().getDefaultRootElement();
  1294. }
  1295. /**
  1296. * Breaks this view on the given axis at the given length.
  1297. *
  1298. * @param axis may be either X_AXIS or Y_AXIS
  1299. * @param len specifies where a break is desired in the span
  1300. * @param the current allocation of the view
  1301. * @return the fragment of the view that represents the given span
  1302. * if the view can be broken, otherwise null
  1303. */
  1304. public View breakView(int axis, float len, Shape a) {
  1305. throw new Error("Can't break root view");
  1306. }
  1307. /**
  1308. * Determines the resizability of the view along the
  1309. * given axis. A value of 0 or less is not resizable.
  1310. *
  1311. * @param axis may be either X_AXIS or Y_AXIS
  1312. * @return the weight
  1313. */
  1314. public int getResizeWeight(int axis) {
  1315. if (view != null) {
  1316. return view.getResizeWeight(axis);
  1317. }
  1318. return 0;
  1319. }
  1320. /**
  1321. <