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