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. * Sets the view size.
  1322. *
  1323. * @param width the width
  1324. * @param height the height
  1325. */
  1326. public void setSize(float width, float height) {
  1327. if (view != null) {
  1328. view.setSize(width, height);
  1329. }
  1330. }
  1331. /**
  1332. * Fetches the container hosting the view. This is useful for
  1333. * things like scheduling a repaint, finding out the host
  1334. * components font, etc. The default implementation
  1335. * of this is to forward the query to the parent view.
  1336. *
  1337. * @return the container
  1338. */
  1339. public Container getContainer() {
  1340. return editor;
  1341. }
  1342. /**
  1343. * Fetches the factory to be used for building the
  1344. * various view fragments that make up the view that
  1345. * represents the model. This is what determines
  1346. * how the model will be represented. This is implemented
  1347. * to fetch the factory provided by the associated
  1348. * EditorKit unless that is null, in which case this
  1349. * simply returns the BasicTextUI itself which allows
  1350. * subclasses to implement a simple factory directly without
  1351. * creating extra objects.
  1352. *
  1353. * @return the factory
  1354. */
  1355. public ViewFactory getViewFactory() {
  1356. EditorKit kit = getEditorKit(editor);
  1357. ViewFactory f = kit.getViewFactory();
  1358. if (f != null) {
  1359. return f;
  1360. }
  1361. return BasicTextUI.this;
  1362. }
  1363. private View view;
  1364. }
  1365. /**
  1366. * Handles updates from various places. If the model is changed,
  1367. * this class unregisters as a listener to the old model and
  1368. * registers with the new model. If the document model changes,
  1369. * the change is forwarded to the root view. If the focus
  1370. * accelerator changes, a new keystroke is registered to request
  1371. * focus.
  1372. */
  1373. class UpdateHandler implements PropertyChangeListener, DocumentListener, LayoutManager2, Runnable, UIResource {
  1374. // --- PropertyChangeListener methods -----------------------
  1375. /**
  1376. * This method gets called when a bound property is changed.
  1377. * We are looking for document changes on the editor.
  1378. */
  1379. public final void propertyChange(PropertyChangeEvent evt) {
  1380. Object oldValue = evt.getOldValue();
  1381. Object newValue = evt.getNewValue();
  1382. if ((oldValue instanceof Document) || (newValue instanceof Document)) {
  1383. if (oldValue != null) {
  1384. ((Document)oldValue).removeDocumentListener(this);
  1385. }
  1386. if (newValue != null) {
  1387. ((Document)newValue).addDocumentListener(this);
  1388. }
  1389. modelChanged();
  1390. }
  1391. if (JTextComponent.FOCUS_ACCELERATOR_KEY.equals
  1392. (evt.getPropertyName())) {
  1393. updateFocusAcceleratorBinding(true);
  1394. } else if ("componentOrientation".equals(evt.getPropertyName())) {
  1395. ComponentOrientation o=(ComponentOrientation)evt.getNewValue();
  1396. Boolean runDir = o.isLeftToRight()
  1397. ? TextAttribute.RUN_DIRECTION_LTR
  1398. : TextAttribute.RUN_DIRECTION_RTL;
  1399. Document doc = editor.getDocument();
  1400. doc.putProperty( TextAttribute.RUN_DIRECTION, runDir );
  1401. modelChanged();
  1402. }
  1403. BasicTextUI.this.propertyChange(evt);
  1404. }
  1405. // --- DocumentListener methods -----------------------
  1406. /**
  1407. * The insert notification. Gets sent to the root of the view structure
  1408. * that represents the portion of the model being represented by the
  1409. * editor. The factory is added as an argument to the update so that
  1410. * the views can update themselves in a dynamic (not hardcoded) way.
  1411. *
  1412. * @param e The change notification from the currently associated
  1413. * document.
  1414. * @see DocumentListener#insertUpdate
  1415. */
  1416. public final void insertUpdate(DocumentEvent e) {
  1417. Document doc = e.getDocument();
  1418. Object o = doc.getProperty("i18n");
  1419. if (o instanceof Boolean) {
  1420. Boolean i18nFlag = (Boolean) o;
  1421. if (i18nFlag.booleanValue() != i18nView) {
  1422. // i18n flag changed, rebuild the view
  1423. i18nView = i18nFlag.booleanValue();
  1424. modelChanged();
  1425. return;
  1426. }
  1427. }
  1428. // normal insert update
  1429. Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
  1430. rootView.insertUpdate(e, alloc, rootView.getViewFactory());
  1431. }
  1432. /**
  1433. * The remove notification. Gets sent to the root of the view structure
  1434. * that represents the portion of the model being represented by the
  1435. * editor. The factory is added as an argument to the update so that
  1436. * the views can update themselves in a dynamic (not hardcoded) way.
  1437. *
  1438. * @param e The change notification from the currently associated
  1439. * document.
  1440. * @see DocumentListener#removeUpdate
  1441. */
  1442. public final void removeUpdate(DocumentEvent e) {
  1443. if ((constraints != null) && (! constraints.isEmpty())) {
  1444. // There may be components to remove. This method may
  1445. // be called by a thread other than the event dispatching
  1446. // thread. We really want to do this before any repaints
  1447. // get scheduled so the order here is important.
  1448. if (SwingUtilities.isEventDispatchThread()) {
  1449. run();
  1450. } else {
  1451. SwingUtilities.invokeLater(this);
  1452. }
  1453. }
  1454. Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
  1455. rootView.removeUpdate(e, alloc, rootView.getViewFactory());
  1456. }
  1457. /**
  1458. * The change notification. Gets sent to the root of the view structure
  1459. * that represents the portion of the model being represented by the
  1460. * editor. The factory is added as an argument to the update so that
  1461. * the views can update themselves in a dynamic (not hardcoded) way.
  1462. *
  1463. * @param e The change notification from the currently associated
  1464. * document.
  1465. * @see DocumentListener#changeUpdate
  1466. */
  1467. public final void changedUpdate(DocumentEvent e) {
  1468. Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
  1469. rootView.changedUpdate(e, alloc, rootView.getViewFactory());
  1470. }
  1471. // --- Runnable methods --------------------------------------
  1472. /**
  1473. * Weed out components that have an association with a View
  1474. * that have been removed.
  1475. */
  1476. public void run() {
  1477. Vector toRemove = null;
  1478. Enumeration components = constraints.keys();
  1479. while (components.hasMoreElements()) {
  1480. Component comp = (Component) components.nextElement();
  1481. View v = (View) constraints.get(comp);
  1482. if (v.getStartOffset() == v.getEndOffset()) {
  1483. // this view has been removed, its coordinates
  1484. // have collapsed down to the removal location.
  1485. if (toRemove == null) {
  1486. toRemove = new Vector();
  1487. }
  1488. toRemove.addElement(comp);
  1489. }
  1490. }
  1491. if (toRemove != null) {
  1492. int n = toRemove.size();
  1493. for (int i = 0; i < n; i++) {
  1494. editor.remove((Component) toRemove.elementAt(i));
  1495. }
  1496. }
  1497. }
  1498. // --- LayoutManager2 methods --------------------------------
  1499. /**
  1500. * Adds the specified component with the specified name to
  1501. * the layout.
  1502. * @param name the component name
  1503. * @param comp the component to be added
  1504. */
  1505. public void addLayoutComponent(String name, Component comp) {
  1506. // not supported
  1507. }
  1508. /**
  1509. * Removes the specified component from the layout.
  1510. * @param comp the component to be removed
  1511. */
  1512. public void removeLayoutComponent(Component comp) {
  1513. if (constraints != null) {
  1514. // remove the constraint record
  1515. constraints.remove(comp);
  1516. }
  1517. }
  1518. /**
  1519. * Calculates the preferred size dimensions for the specified
  1520. * panel given the components in the specified parent container.
  1521. * @param parent the component to be laid out
  1522. *
  1523. * @see #minimumLayoutSize
  1524. */
  1525. public Dimension preferredLayoutSize(Container parent) {
  1526. // should not be called (JComponent uses UI instead)
  1527. return null;
  1528. }
  1529. /**
  1530. * Calculates the minimum size dimensions for the specified
  1531. * panel given the components in the specified parent container.
  1532. * @param parent the component to be laid out
  1533. * @see #preferredLayoutSize
  1534. */
  1535. public Dimension minimumLayoutSize(Container parent) {
  1536. // should not be called (JComponent uses UI instead)
  1537. return null;
  1538. }
  1539. /**
  1540. * Lays out the container in the specified panel. This is
  1541. * implemented to position all components that were added
  1542. * with a View object as a constraint. The current allocation
  1543. * of the associated View is used as the location of the
  1544. * component.
  1545. * <p>
  1546. * A read-lock is acquired on the document to prevent the
  1547. * view tree from being modified while the layout process
  1548. * is active.
  1549. *
  1550. * @param parent the component which needs to be laid out
  1551. */
  1552. public void layoutContainer(Container parent) {
  1553. if ((constraints != null) && (! constraints.isEmpty())) {
  1554. Rectangle alloc = getVisibleEditorRect();
  1555. if (alloc != null) {
  1556. Document doc = editor.getDocument();
  1557. if (doc instanceof AbstractDocument) {
  1558. ((AbstractDocument)doc).readLock();
  1559. }
  1560. try {
  1561. rootView.setSize(alloc.width, alloc.height);
  1562. Enumeration components = constraints.keys();
  1563. while (components.hasMoreElements()) {
  1564. Component comp = (Component) components.nextElement();
  1565. View v = (View) constraints.get(comp);
  1566. Shape ca = calculateViewPosition(alloc, v);
  1567. if (ca != null) {
  1568. Rectangle compAlloc = (ca instanceof Rectangle) ?
  1569. (Rectangle) ca : ca.getBounds();
  1570. comp.setBounds(compAlloc);
  1571. }
  1572. }
  1573. } finally {
  1574. if (doc instanceof AbstractDocument) {
  1575. ((AbstractDocument)doc).readUnlock();
  1576. }
  1577. }
  1578. }
  1579. }
  1580. }
  1581. /**
  1582. * Find the Shape representing the given view.
  1583. */
  1584. Shape calculateViewPosition(Shape alloc, View v) {
  1585. int pos = v.getStartOffset();
  1586. View child = null;
  1587. for (View parent = rootView; (parent != null) && (parent != v); parent = child) {
  1588. int index = parent.getViewIndex(pos, Position.Bias.Forward);
  1589. alloc = parent.getChildAllocation(index, alloc);
  1590. child = parent.getView(index);
  1591. }
  1592. return (child != null) ? alloc : null;
  1593. }
  1594. /**
  1595. * Adds the specified component to the layout, using the specified
  1596. * constraint object. We only store those components that were added
  1597. * with a constraint that is of type View.
  1598. *
  1599. * @param comp the component to be added
  1600. * @param constraint where/how the component is added to the layout.
  1601. */
  1602. public void addLayoutComponent(Component comp, Object constraint) {
  1603. if (constraint instanceof View) {
  1604. if (constraints == null) {
  1605. constraints = new Hashtable(7);
  1606. }
  1607. constraints.put(comp, constraint);
  1608. }
  1609. }
  1610. /**
  1611. * Returns the maximum size of this component.
  1612. * @see java.awt.Component#getMinimumSize()
  1613. * @see java.awt.Component#getPreferredSize()
  1614. * @see LayoutManager
  1615. */
  1616. public Dimension maximumLayoutSize(Container target) {
  1617. // should not be called (JComponent uses UI instead)
  1618. return null;
  1619. }
  1620. /**
  1621. * Returns the alignment along the x axis. This specifies how
  1622. * the component would like to be aligned relative to other
  1623. * components. The value should be a number between 0 and 1
  1624. * where 0 represents alignment along the origin, 1 is aligned
  1625. * the furthest away from the origin, 0.5 is centered, etc.
  1626. */
  1627. public float getLayoutAlignmentX(Container target) {
  1628. return 0.5f;
  1629. }
  1630. /**
  1631. * Returns the alignment along the y axis. This specifies how
  1632. * the component would like to be aligned relative to other
  1633. * components. The value should be a number between 0 and 1
  1634. * where 0 represents alignment along the origin, 1 is aligned
  1635. * the furthest away from the origin, 0.5 is centered, etc.
  1636. */
  1637. public float getLayoutAlignmentY(Container target) {
  1638. return 0.5f;
  1639. }
  1640. /**
  1641. * Invalidates the layout, indicating that if the layout manager
  1642. * has cached information it should be discarded.
  1643. */
  1644. public void invalidateLayout(Container target) {
  1645. }
  1646. /**
  1647. * The "layout constraints" for the LayoutManager2 implementation.
  1648. * These are View objects for those components that are represented
  1649. * by a View in the View tree.
  1650. */
  1651. private Hashtable constraints;
  1652. private boolean i18nView = false;
  1653. }
  1654. /**
  1655. * Registered in the ActionMap.
  1656. */
  1657. class FocusAction extends AbstractAction {
  1658. public void actionPerformed(ActionEvent e) {
  1659. editor.requestFocus();
  1660. }
  1661. public boolean isEnabled() {
  1662. return editor.isEditable();
  1663. }
  1664. }
  1665. }