1. /*
  2. * @(#)BasicTextUI.java 1.103 04/05/26
  3. *
  4. * Copyright 2004 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.util.*;
  9. import java.awt.*;
  10. import java.awt.event.*;
  11. import java.awt.font.*;
  12. import java.awt.datatransfer.*;
  13. import java.awt.dnd.*;
  14. import java.awt.im.InputContext;
  15. import java.beans.*;
  16. import java.io.*;
  17. import java.net.*;
  18. import javax.swing.*;
  19. import javax.swing.plaf.*;
  20. import javax.swing.text.*;
  21. import javax.swing.event.*;
  22. import javax.swing.border.Border;
  23. import javax.swing.plaf.UIResource;
  24. import sun.awt.AppContext;
  25. import sun.swing.DefaultLookup;
  26. import com.sun.java.swing.SwingUtilities2;
  27. /**
  28. * <p>
  29. * Basis of a text components look-and-feel. This provides the
  30. * basic editor view and controller services that may be useful
  31. * when creating a look-and-feel for an extension of
  32. * <code>JTextComponent</code>.
  33. * <p>
  34. * Most state is held in the associated <code>JTextComponent</code>
  35. * as bound properties, and the UI installs default values for the
  36. * various properties. This default will install something for
  37. * all of the properties. Typically, a LAF implementation will
  38. * do more however. At a minimum, a LAF would generally install
  39. * key bindings.
  40. * <p>
  41. * This class also provides some concurrency support if the
  42. * <code>Document</code> associated with the JTextComponent is a subclass of
  43. * <code>AbstractDocument</code>. Access to the View (or View hierarchy) is
  44. * serialized between any thread mutating the model and the Swing
  45. * event thread (which is expected to render, do model/view coordinate
  46. * translation, etc). <em>Any access to the root view should first
  47. * acquire a read-lock on the AbstractDocument and release that lock
  48. * in a finally block.</em>
  49. * <p>
  50. * An important method to define is the {@link #getPropertyPrefix} method
  51. * which is used as the basis of the keys used to fetch defaults
  52. * from the UIManager. The string should reflect the type of
  53. * TextUI (eg. TextField, TextArea, etc) without the particular
  54. * LAF part of the name (eg Metal, Motif, etc).
  55. * <p>
  56. * To build a view of the model, one of the following strategies
  57. * can be employed.
  58. * <ol>
  59. * <li>
  60. * One strategy is to simply redefine the
  61. * ViewFactory interface in the UI. By default, this UI itself acts
  62. * as the factory for View implementations. This is useful
  63. * for simple factories. To do this reimplement the
  64. * {@link #create} method.
  65. * <li>
  66. * A common strategy for creating more complex types of documents
  67. * is to have the EditorKit implementation return a factory. Since
  68. * the EditorKit ties all of the pieces necessary to maintain a type
  69. * of document, the factory is typically an important part of that
  70. * and should be produced by the EditorKit implementation.
  71. * <li>
  72. * A less common way to create more complex types is to have
  73. * the UI implementation create a.
  74. * separate object for the factory. To do this, the
  75. * {@link #createViewFactory} method should be reimplemented to
  76. * return some factory.
  77. * </ol>
  78. * <p>
  79. * <strong>Warning:</strong>
  80. * Serialized objects of this class will not be compatible with
  81. * future Swing releases. The current serialization support is
  82. * appropriate for short term storage or RMI between applications running
  83. * the same version of Swing. As of 1.4, support for long term storage
  84. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  85. * has been added to the <code>java.beans</code> package.
  86. * Please see {@link java.beans.XMLEncoder}.
  87. *
  88. * @author Timothy Prinzing
  89. * @version 1.103 05/26/04
  90. */
  91. public abstract class BasicTextUI extends TextUI implements ViewFactory {
  92. /**
  93. * Creates a new UI.
  94. */
  95. public BasicTextUI() {
  96. painted = false;
  97. }
  98. /**
  99. * Creates the object to use for a caret. By default an
  100. * instance of BasicCaret is created. This method
  101. * can be redefined to provide something else that implements
  102. * the InputPosition interface or a subclass of JCaret.
  103. *
  104. * @return the caret object
  105. */
  106. protected Caret createCaret() {
  107. return new BasicCaret();
  108. }
  109. /**
  110. * Creates the object to use for adding highlights. By default
  111. * an instance of BasicHighlighter is created. This method
  112. * can be redefined to provide something else that implements
  113. * the Highlighter interface or a subclass of DefaultHighlighter.
  114. *
  115. * @return the highlighter
  116. */
  117. protected Highlighter createHighlighter() {
  118. return new BasicHighlighter();
  119. }
  120. /**
  121. * Fetches the name of the keymap that will be installed/used
  122. * by default for this UI. This is implemented to create a
  123. * name based upon the classname. The name is the the name
  124. * of the class with the package prefix removed.
  125. *
  126. * @return the name
  127. */
  128. protected String getKeymapName() {
  129. String nm = getClass().getName();
  130. int index = nm.lastIndexOf('.');
  131. if (index >= 0) {
  132. nm = nm.substring(index+1, nm.length());
  133. }
  134. return nm;
  135. }
  136. /**
  137. * Creates the keymap to use for the text component, and installs
  138. * any necessary bindings into it. By default, the keymap is
  139. * shared between all instances of this type of TextUI. The
  140. * keymap has the name defined by the getKeymapName method. If the
  141. * keymap is not found, then DEFAULT_KEYMAP from JTextComponent is used.
  142. * <p>
  143. * The set of bindings used to create the keymap is fetched
  144. * from the UIManager using a key formed by combining the
  145. * {@link #getPropertyPrefix} method
  146. * and the string <code>.keyBindings</code>. The type is expected
  147. * to be <code>JTextComponent.KeyBinding[]</code>.
  148. *
  149. * @return the keymap
  150. * @see #getKeymapName
  151. * @see javax.swing.text.JTextComponent
  152. */
  153. protected Keymap createKeymap() {
  154. String nm = getKeymapName();
  155. Keymap map = JTextComponent.getKeymap(nm);
  156. if (map == null) {
  157. Keymap parent = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
  158. map = JTextComponent.addKeymap(nm, parent);
  159. String prefix = getPropertyPrefix();
  160. Object o = DefaultLookup.get(editor, this,
  161. prefix + ".keyBindings");
  162. if ((o != null) && (o instanceof JTextComponent.KeyBinding[])) {
  163. JTextComponent.KeyBinding[] bindings = (JTextComponent.KeyBinding[]) o;
  164. JTextComponent.loadKeymap(map, bindings, getComponent().getActions());
  165. }
  166. }
  167. return map;
  168. }
  169. /**
  170. * This method gets called when a bound property is changed
  171. * on the associated JTextComponent. This is a hook
  172. * which UI implementations may change to reflect how the
  173. * UI displays bound properties of JTextComponent subclasses.
  174. * This is implemented to do nothing (i.e. the response to
  175. * properties in JTextComponent itself are handled prior
  176. * to calling this method).
  177. *
  178. * @param evt the property change event
  179. */
  180. protected void propertyChange(PropertyChangeEvent evt) {
  181. }
  182. /**
  183. * Gets the name used as a key to look up properties through the
  184. * UIManager. This is used as a prefix to all the standard
  185. * text properties.
  186. *
  187. * @return the name
  188. */
  189. protected abstract String getPropertyPrefix();
  190. /**
  191. * Initializes component properties, e.g. font, foreground,
  192. * background, caret color, selection color, selected text color,
  193. * disabled text color, and border color. The font, foreground, and
  194. * background properties are only set if their current value is either null
  195. * or a UIResource, other properties are set if the current
  196. * value is null.
  197. *
  198. * @see #uninstallDefaults
  199. * @see #installUI
  200. */
  201. protected void installDefaults()
  202. {
  203. String prefix = getPropertyPrefix();
  204. Font f = editor.getFont();
  205. if ((f == null) || (f instanceof UIResource)) {
  206. editor.setFont(UIManager.getFont(prefix + ".font"));
  207. }
  208. Color bg = editor.getBackground();
  209. if ((bg == null) || (bg instanceof UIResource)) {
  210. editor.setBackground(UIManager.getColor(prefix + ".background"));
  211. }
  212. Color fg = editor.getForeground();
  213. if ((fg == null) || (fg instanceof UIResource)) {
  214. editor.setForeground(UIManager.getColor(prefix + ".foreground"));
  215. }
  216. Color color = editor.getCaretColor();
  217. if ((color == null) || (color instanceof UIResource)) {
  218. editor.setCaretColor(UIManager.getColor(prefix + ".caretForeground"));
  219. }
  220. Color s = editor.getSelectionColor();
  221. if ((s == null) || (s instanceof UIResource)) {
  222. editor.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground"));
  223. }
  224. Color sfg = editor.getSelectedTextColor();
  225. if ((sfg == null) || (sfg instanceof UIResource)) {
  226. editor.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground"));
  227. }
  228. Color dfg = editor.getDisabledTextColor();
  229. if ((dfg == null) || (dfg instanceof UIResource)) {
  230. editor.setDisabledTextColor(UIManager.getColor(prefix + ".inactiveForeground"));
  231. }
  232. Border b = editor.getBorder();
  233. if ((b == null) || (b instanceof UIResource)) {
  234. editor.setBorder(UIManager.getBorder(prefix + ".border"));
  235. }
  236. Insets margin = editor.getMargin();
  237. if (margin == null || margin instanceof UIResource) {
  238. editor.setMargin(UIManager.getInsets(prefix + ".margin"));
  239. }
  240. }
  241. private void installDefaults2() {
  242. editor.addMouseListener(defaultDragRecognizer);
  243. editor.addMouseMotionListener(defaultDragRecognizer);
  244. String prefix = getPropertyPrefix();
  245. Caret caret = editor.getCaret();
  246. if (caret == null || caret instanceof UIResource) {
  247. caret = createCaret();
  248. editor.setCaret(caret);
  249. int rate = DefaultLookup.getInt(getComponent(), this, prefix + ".caretBlinkRate", 500);
  250. caret.setBlinkRate(rate);
  251. }
  252. Highlighter highlighter = editor.getHighlighter();
  253. if (highlighter == null || highlighter instanceof UIResource) {
  254. editor.setHighlighter(createHighlighter());
  255. }
  256. TransferHandler th = editor.getTransferHandler();
  257. if (th == null || th instanceof UIResource) {
  258. editor.setTransferHandler(getTransferHandler());
  259. }
  260. DropTarget dropTarget = editor.getDropTarget();
  261. if (dropTarget instanceof UIResource) {
  262. if (defaultDropTargetListener == null) {
  263. defaultDropTargetListener = new TextDropTargetListener();
  264. }
  265. try {
  266. dropTarget.addDropTargetListener(defaultDropTargetListener);
  267. } catch (TooManyListenersException tmle) {
  268. // should not happen... swing drop target is multicast
  269. }
  270. }
  271. }
  272. /**
  273. * Sets the component properties that haven't been explicitly overridden to
  274. * null. A property is considered overridden if its current value
  275. * is not a UIResource.
  276. *
  277. * @see #installDefaults
  278. * @see #uninstallUI
  279. */
  280. protected void uninstallDefaults()
  281. {
  282. editor.removeMouseListener(defaultDragRecognizer);
  283. editor.removeMouseMotionListener(defaultDragRecognizer);
  284. if (editor.getCaretColor() instanceof UIResource) {
  285. editor.setCaretColor(null);
  286. }
  287. if (editor.getSelectionColor() instanceof UIResource) {
  288. editor.setSelectionColor(null);
  289. }
  290. if (editor.getDisabledTextColor() instanceof UIResource) {
  291. editor.setDisabledTextColor(null);
  292. }
  293. if (editor.getSelectedTextColor() instanceof UIResource) {
  294. editor.setSelectedTextColor(null);
  295. }
  296. if (editor.getBorder() instanceof UIResource) {
  297. editor.setBorder(null);
  298. }
  299. if (editor.getMargin() instanceof UIResource) {
  300. editor.setMargin(null);
  301. }
  302. if (editor.getCaret() instanceof UIResource) {
  303. editor.setCaret(null);
  304. }
  305. if (editor.getHighlighter() instanceof UIResource) {
  306. editor.setHighlighter(null);
  307. }
  308. if (editor.getTransferHandler() instanceof UIResource) {
  309. editor.setTransferHandler(null);
  310. }
  311. }
  312. /**
  313. * Installs listeners for the UI.
  314. */
  315. protected void installListeners() {
  316. }
  317. /**
  318. * Uninstalls listeners for the UI.
  319. */
  320. protected void uninstallListeners() {
  321. }
  322. protected void installKeyboardActions() {
  323. // backward compatibility support... keymaps for the UI
  324. // are now installed in the more friendly input map.
  325. editor.setKeymap(createKeymap());
  326. InputMap km = getInputMap();
  327. if (km != null) {
  328. SwingUtilities.replaceUIInputMap(editor, JComponent.WHEN_FOCUSED,
  329. km);
  330. }
  331. ActionMap map = getActionMap();
  332. if (map != null) {
  333. SwingUtilities.replaceUIActionMap(editor, map);
  334. }
  335. updateFocusAcceleratorBinding(false);
  336. }
  337. /**
  338. * Get the InputMap to use for the UI.
  339. */
  340. InputMap getInputMap() {
  341. InputMap map = new InputMapUIResource();
  342. InputMap shared =
  343. (InputMap)DefaultLookup.get(editor, this,
  344. getPropertyPrefix() + ".focusInputMap");
  345. if (shared != null) {
  346. map.setParent(shared);
  347. }
  348. return map;
  349. }
  350. /**
  351. * Invoked when the focus accelerator changes, this will update the
  352. * key bindings as necessary.
  353. */
  354. void updateFocusAcceleratorBinding(boolean changed) {
  355. char accelerator = editor.getFocusAccelerator();
  356. if (changed || accelerator != '\0') {
  357. InputMap km = SwingUtilities.getUIInputMap
  358. (editor, JComponent.WHEN_IN_FOCUSED_WINDOW);
  359. if (km == null && accelerator != '\0') {
  360. km = new ComponentInputMapUIResource(editor);
  361. SwingUtilities.replaceUIInputMap(editor, JComponent.
  362. WHEN_IN_FOCUSED_WINDOW, km);
  363. ActionMap am = getActionMap();
  364. SwingUtilities.replaceUIActionMap(editor, am);
  365. }
  366. if (km != null) {
  367. km.clear();
  368. if (accelerator != '\0') {
  369. km.put(KeyStroke.getKeyStroke(accelerator,
  370. ActionEvent.ALT_MASK),
  371. "requestFocus");
  372. }
  373. }
  374. }
  375. }
  376. /**
  377. * Invoked when editable property is changed.
  378. *
  379. * removing 'TAB' and 'SHIFT-TAB' from traversalKeysSet in case
  380. * editor is editable
  381. * adding 'TAB' and 'SHIFT-TAB' to traversalKeysSet in case
  382. * editor is non editable
  383. */
  384. void updateFocusTraversalKeys() {
  385. /*
  386. * Fix for 4514331 Non-editable JTextArea and similar
  387. * should allow Tab to keyboard - accessibility
  388. */
  389. EditorKit editorKit = getEditorKit(editor);
  390. if ( editorKit != null
  391. && editorKit instanceof DefaultEditorKit) {
  392. Set storedForwardTraversalKeys = editor.
  393. getFocusTraversalKeys(KeyboardFocusManager.
  394. FORWARD_TRAVERSAL_KEYS);
  395. Set storedBackwardTraversalKeys = editor.
  396. getFocusTraversalKeys(KeyboardFocusManager.
  397. BACKWARD_TRAVERSAL_KEYS);
  398. Set forwardTraversalKeys =
  399. new HashSet(storedForwardTraversalKeys);
  400. Set backwardTraversalKeys =
  401. new HashSet(storedBackwardTraversalKeys);
  402. if (editor.isEditable()) {
  403. forwardTraversalKeys.
  404. remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
  405. backwardTraversalKeys.
  406. remove(KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
  407. InputEvent.SHIFT_MASK));
  408. } else {
  409. forwardTraversalKeys.add(KeyStroke.
  410. getKeyStroke(KeyEvent.VK_TAB, 0));
  411. backwardTraversalKeys.
  412. add(KeyStroke.
  413. getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));
  414. }
  415. LookAndFeel.installProperty(editor,
  416. "focusTraversalKeysForward",
  417. forwardTraversalKeys);
  418. LookAndFeel.installProperty(editor,
  419. "focusTraversalKeysBackward",
  420. backwardTraversalKeys);
  421. }
  422. }
  423. /**
  424. * Returns the <code>TransferHandler</code> that will be installed if
  425. * their isn't one installed on the <code>JTextComponent</code>.
  426. */
  427. TransferHandler getTransferHandler() {
  428. return defaultTransferHandler;
  429. }
  430. /**
  431. * Fetch an action map to use.
  432. */
  433. ActionMap getActionMap() {
  434. String mapName = getPropertyPrefix() + ".actionMap";
  435. ActionMap map = (ActionMap)UIManager.get(mapName);
  436. if (map == null) {
  437. map = createActionMap();
  438. if (map != null) {
  439. UIManager.getLookAndFeelDefaults().put(mapName, map);
  440. }
  441. }
  442. ActionMap componentMap = new ActionMapUIResource();
  443. componentMap.put("requestFocus", new FocusAction());
  444. /*
  445. * fix for bug 4515750
  446. * JTextField & non-editable JTextArea bind return key - default btn not accessible
  447. *
  448. * Wrap the return action so that it is only enabled when the
  449. * component is editable. This allows the default button to be
  450. * processed when the text component has focus and isn't editable.
  451. *
  452. */
  453. if (getEditorKit(editor) instanceof DefaultEditorKit) {
  454. if (map != null) {
  455. Object obj = map.get(DefaultEditorKit.insertBreakAction);
  456. if (obj != null
  457. && obj instanceof DefaultEditorKit.InsertBreakAction) {
  458. Action action = new TextActionWrapper((TextAction)obj);
  459. componentMap.put(action.getValue(Action.NAME),action);
  460. }
  461. }
  462. }
  463. if (map != null) {
  464. componentMap.setParent(map);
  465. }
  466. return componentMap;
  467. }
  468. /**
  469. * Create a default action map. This is basically the
  470. * set of actions found exported by the component.
  471. */
  472. ActionMap createActionMap() {
  473. ActionMap map = new ActionMapUIResource();
  474. Action[] actions = editor.getActions();
  475. //System.out.println("building map for UI: " + getPropertyPrefix());
  476. int n = actions.length;
  477. for (int i = 0; i < n; i++) {
  478. Action a = actions[i];
  479. map.put(a.getValue(Action.NAME), a);
  480. //System.out.println(" " + a.getValue(Action.NAME));
  481. }
  482. map.put(TransferHandler.getCutAction().getValue(Action.NAME),
  483. TransferHandler.getCutAction());
  484. map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
  485. TransferHandler.getCopyAction());
  486. map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
  487. TransferHandler.getPasteAction());
  488. return map;
  489. }
  490. protected void uninstallKeyboardActions() {
  491. editor.setKeymap(null);
  492. SwingUtilities.replaceUIInputMap(editor, JComponent.
  493. WHEN_IN_FOCUSED_WINDOW, null);
  494. SwingUtilities.replaceUIActionMap(editor, null);
  495. }
  496. /**
  497. * Paints a background for the view. This will only be
  498. * called if isOpaque() on the associated component is
  499. * true. The default is to paint the background color
  500. * of the component.
  501. *
  502. * @param g the graphics context
  503. */
  504. protected void paintBackground(Graphics g) {
  505. g.setColor(editor.getBackground());
  506. g.fillRect(0, 0, editor.getWidth(), editor.getHeight());
  507. }
  508. /**
  509. * Fetches the text component associated with this
  510. * UI implementation. This will be null until
  511. * the ui has been installed.
  512. *
  513. * @return the editor component
  514. */
  515. protected final JTextComponent getComponent() {
  516. return editor;
  517. }
  518. /**
  519. * Flags model changes.
  520. * This is called whenever the model has changed.
  521. * It is implemented to rebuild the view hierarchy
  522. * to represent the default root element of the
  523. * associated model.
  524. */
  525. protected void modelChanged() {
  526. // create a view hierarchy
  527. ViewFactory f = rootView.getViewFactory();
  528. Document doc = editor.getDocument();
  529. Element elem = doc.getDefaultRootElement();
  530. setView(f.create(elem));
  531. }
  532. /**
  533. * Sets the current root of the view hierarchy and calls invalidate().
  534. * If there were any child components, they will be removed (i.e.
  535. * there are assumed to have come from components embedded in views).
  536. *
  537. * @param v the root view
  538. */
  539. protected final void setView(View v) {
  540. rootView.setView(v);
  541. painted = false;
  542. editor.revalidate();
  543. editor.repaint();
  544. }
  545. /**
  546. * Paints the interface safely with a guarantee that
  547. * the model won't change from the view of this thread.
  548. * This does the following things, rendering from
  549. * back to front.
  550. * <ol>
  551. * <li>
  552. * If the component is marked as opaque, the background
  553. * is painted in the current background color of the
  554. * component.
  555. * <li>
  556. * The highlights (if any) are painted.
  557. * <li>
  558. * The view hierarchy is painted.
  559. * <li>
  560. * The caret is painted.
  561. * </ol>
  562. *
  563. * @param g the graphics context
  564. */
  565. protected void paintSafely(Graphics g) {
  566. painted = true;
  567. Highlighter highlighter = editor.getHighlighter();
  568. Caret caret = editor.getCaret();
  569. // paint the background
  570. if (editor.isOpaque()) {
  571. paintBackground(g);
  572. }
  573. // paint the highlights
  574. if (highlighter != null) {
  575. highlighter.paint(g);
  576. }
  577. // paint the view hierarchy
  578. Rectangle alloc = getVisibleEditorRect();
  579. if (alloc != null) {
  580. rootView.paint(g, alloc);
  581. }
  582. // paint the caret
  583. if (caret != null) {
  584. caret.paint(g);
  585. }
  586. }
  587. // --- ComponentUI methods --------------------------------------------
  588. /**
  589. * Installs the UI for a component. This does the following
  590. * things.
  591. * <ol>
  592. * <li>
  593. * Set the associated component to opaque (can be changed
  594. * easily by a subclass or on JTextComponent directly),
  595. * which is the most common case. This will cause the
  596. * component's background color to be painted.
  597. * <li>
  598. * Install the default caret and highlighter into the
  599. * associated component.
  600. * <li>
  601. * Attach to the editor and model. If there is no
  602. * model, a default one is created.
  603. * <li>
  604. * create the view factory and the view hierarchy used
  605. * to represent the model.
  606. * </ol>
  607. *
  608. * @param c the editor component
  609. * @see ComponentUI#installUI
  610. */
  611. public void installUI(JComponent c) {
  612. if (c instanceof JTextComponent) {
  613. editor = (JTextComponent) c;
  614. // install defaults
  615. installDefaults();
  616. installDefaults2();
  617. // common case is background painted... this can
  618. // easily be changed by subclasses or from outside
  619. // of the component.
  620. LookAndFeel.installProperty(editor, "opaque", Boolean.TRUE);
  621. LookAndFeel.installProperty(editor, "autoscrolls", Boolean.TRUE);
  622. // attach to the model and editor
  623. editor.addPropertyChangeListener(updateHandler);
  624. Document doc = editor.getDocument();
  625. if (doc == null) {
  626. // no model, create a default one. This will
  627. // fire a notification to the updateHandler
  628. // which takes care of the rest.
  629. editor.setDocument(getEditorKit(editor).createDefaultDocument());
  630. } else {
  631. doc.addDocumentListener(updateHandler);
  632. modelChanged();
  633. }
  634. // install keymap
  635. installListeners();
  636. installKeyboardActions();
  637. LayoutManager oldLayout = editor.getLayout();
  638. if ((oldLayout == null) || (oldLayout instanceof UIResource)) {
  639. // by default, use default LayoutManger implementation that
  640. // will position the components associated with a View object.
  641. editor.setLayout(updateHandler);
  642. }
  643. } else {
  644. throw new Error("TextUI needs JTextComponent");
  645. }
  646. }
  647. /**
  648. * Deinstalls the UI for a component. This removes the listeners,
  649. * uninstalls the highlighter, removes views, and nulls out the keymap.
  650. *
  651. * @param c the editor component
  652. * @see ComponentUI#uninstallUI
  653. */
  654. public void uninstallUI(JComponent c) {
  655. // detach from the model
  656. editor.removePropertyChangeListener(updateHandler);
  657. editor.getDocument().removeDocumentListener(updateHandler);
  658. // view part
  659. painted = false;
  660. uninstallDefaults();
  661. rootView.setView(null);
  662. c.removeAll();
  663. LayoutManager lm = c.getLayout();
  664. if (lm instanceof UIResource) {
  665. c.setLayout(null);
  666. }
  667. // controller part
  668. uninstallKeyboardActions();
  669. uninstallListeners();
  670. editor = null;
  671. }
  672. /**
  673. * Superclass paints background in an uncontrollable way
  674. * (i.e. one might want an image tiled into the background).
  675. * To prevent this from happening twice, this method is
  676. * reimplemented to simply paint.
  677. * <p>
  678. * <em>NOTE:</em> Superclass is also not thread-safe in
  679. * it's rendering of the background, although that's not
  680. * an issue with the default rendering.
  681. */
  682. public void update(Graphics g, JComponent c) {
  683. paint(g, c);
  684. }
  685. /**
  686. * Paints the interface. This is routed to the
  687. * paintSafely method under the guarantee that
  688. * the model won't change from the view of this thread
  689. * while it's rendering (if the associated model is
  690. * derived from AbstractDocument). This enables the
  691. * model to potentially be updated asynchronously.
  692. *
  693. * @param g the graphics context
  694. * @param c the editor component
  695. */
  696. public final void paint(Graphics g, JComponent c) {
  697. if ((rootView.getViewCount() > 0) && (rootView.getView(0) != null)) {
  698. Graphics2D g2d = SwingUtilities2.getGraphics2D(g);
  699. boolean frcStored = false;
  700. if (g2d != null) {
  701. FontRenderContext deviceFRC = g2d.getFontRenderContext();
  702. FontRenderContext frc = SwingUtilities2.getFontRenderContext(c);
  703. if (!SwingUtilities2.
  704. isFontRenderContextCompatible(deviceFRC,frc)) {
  705. AppContext.getAppContext().put(SwingUtilities2.FRC_KEY,frc);
  706. frcStored = true;
  707. }
  708. }
  709. Document doc = editor.getDocument();
  710. if (doc instanceof AbstractDocument) {
  711. ((AbstractDocument)doc).readLock();
  712. }
  713. try {
  714. paintSafely(g);
  715. } finally {
  716. if (doc instanceof AbstractDocument) {
  717. ((AbstractDocument)doc).readUnlock();
  718. }
  719. if (frcStored) {
  720. AppContext.getAppContext().remove(SwingUtilities2.FRC_KEY);
  721. }
  722. }
  723. }
  724. }
  725. /**
  726. * Gets the preferred size for the editor component. If the component
  727. * has been given a size prior to receiving this request, it will
  728. * set the size of the view hierarchy to reflect the size of the component
  729. * before requesting the preferred size of the view hierarchy. This
  730. * allows formatted views to format to the current component size before
  731. * answering the request. Other views don't care about currently formatted
  732. * size and give the same answer either way.
  733. *
  734. * @param c the editor component
  735. * @return the size
  736. */
  737. public Dimension getPreferredSize(JComponent c) {
  738. Document doc = editor.getDocument();
  739. Insets i = c.getInsets();
  740. Dimension d = c.getSize();
  741. if (doc instanceof AbstractDocument) {
  742. ((AbstractDocument)doc).readLock();
  743. }
  744. try {
  745. if ((d.width > (i.left + i.right)) && (d.height > (i.top + i.bottom))) {
  746. rootView.setSize(d.width - i.left - i.right, d.height - i.top - i.bottom);
  747. }
  748. else if (d.width == 0 && d.height == 0) {
  749. // Probably haven't been layed out yet, force some sort of
  750. // initial sizing.
  751. rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
  752. }
  753. d.width = (int) Math.min((long) rootView.getPreferredSpan(View.X_AXIS) +
  754. (long) i.left + (long) i.right, Integer.MAX_VALUE);
  755. d.height = (int) Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) +
  756. (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
  757. } finally {
  758. if (doc instanceof AbstractDocument) {
  759. ((AbstractDocument)doc).readUnlock();
  760. }
  761. }
  762. return d;
  763. }
  764. /**
  765. * Gets the minimum size for the editor component.
  766. *
  767. * @param c the editor component
  768. * @return the size
  769. */
  770. public Dimension getMinimumSize(JComponent c) {
  771. Document doc = editor.getDocument();
  772. Insets i = c.getInsets();
  773. Dimension d = new Dimension();
  774. if (doc instanceof AbstractDocument) {
  775. ((AbstractDocument)doc).readLock();
  776. }
  777. try {
  778. d.width = (int) rootView.getMinimumSpan(View.X_AXIS) + i.left + i.right;
  779. d.height = (int) rootView.getMinimumSpan(View.Y_AXIS) + i.top + i.bottom;
  780. } finally {
  781. if (doc instanceof AbstractDocument) {
  782. ((AbstractDocument)doc).readUnlock();
  783. }
  784. }
  785. return d;
  786. }
  787. /**
  788. * Gets the maximum size for the editor component.
  789. *
  790. * @param c the editor component
  791. * @return the size
  792. */
  793. public Dimension getMaximumSize(JComponent c) {
  794. Document doc = editor.getDocument();
  795. Insets i = c.getInsets();
  796. Dimension d = new Dimension();
  797. if (doc instanceof AbstractDocument) {
  798. ((AbstractDocument)doc).readLock();
  799. }
  800. try {
  801. d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS) +
  802. (long) i.left + (long) i.right, Integer.MAX_VALUE);
  803. d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS) +
  804. (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
  805. } finally {
  806. if (doc instanceof AbstractDocument) {
  807. ((AbstractDocument)doc).readUnlock();
  808. }
  809. }
  810. return d;
  811. }
  812. // ---- TextUI methods -------------------------------------------
  813. /**
  814. * Gets the allocation to give the root View. Due
  815. * to an unfortunate set of historical events this
  816. * method is inappropriately named. The Rectangle
  817. * returned has nothing to do with visibility.
  818. * The component must have a non-zero positive size for
  819. * this translation to be computed.
  820. *
  821. * @return the bounding box for the root view
  822. */
  823. protected Rectangle getVisibleEditorRect() {
  824. Rectangle alloc = editor.getBounds();
  825. if ((alloc.width > 0) && (alloc.height > 0)) {
  826. alloc.x = alloc.y = 0;
  827. Insets insets = editor.getInsets();
  828. alloc.x += insets.left;
  829. alloc.y += insets.top;
  830. alloc.width -= insets.left + insets.right;
  831. alloc.height -= insets.top + insets.bottom;
  832. return alloc;
  833. }
  834. return null;
  835. }
  836. /**
  837. * Converts the given location in the model to a place in
  838. * the view coordinate system.
  839. * The component must have a non-zero positive size for
  840. * this translation to be computed.
  841. *
  842. * @param tc the text component for which this UI is installed
  843. * @param pos the local location in the model to translate >= 0
  844. * @return the coordinates as a rectangle, null if the model is not painted
  845. * @exception BadLocationException if the given position does not
  846. * represent a valid location in the associated document
  847. * @see TextUI#modelToView
  848. */
  849. public Rectangle modelToView(JTextComponent tc, int pos) throws BadLocationException {
  850. return modelToView(tc, pos, Position.Bias.Forward);
  851. }
  852. /**
  853. * Converts the given location in the model to a place in
  854. * the view coordinate system.
  855. * The component must have a non-zero positive size for
  856. * this translation to be computed.
  857. *
  858. * @param tc the text component for which this UI is installed
  859. * @param pos the local location in the model to translate >= 0
  860. * @return the coordinates as a rectangle, null if the model is not painted
  861. * @exception BadLocationException if the given position does not
  862. * represent a valid location in the associated document
  863. * @see TextUI#modelToView
  864. */
  865. public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias) throws BadLocationException {
  866. Document doc = editor.getDocument();
  867. if (doc instanceof AbstractDocument) {
  868. ((AbstractDocument)doc).readLock();
  869. }
  870. try {
  871. Rectangle alloc = getVisibleEditorRect();
  872. if (alloc != null) {
  873. rootView.setSize(alloc.width, alloc.height);
  874. Shape s = rootView.modelToView(pos, alloc, bias);
  875. if (s != null) {
  876. return s.getBounds();
  877. }
  878. }
  879. } finally {
  880. if (doc instanceof AbstractDocument) {
  881. ((AbstractDocument)doc).readUnlock();
  882. }
  883. }
  884. return null;
  885. }
  886. /**
  887. * Converts the given place in the view coordinate system
  888. * to the nearest representative location in the model.
  889. * The component must have a non-zero positive size for
  890. * this translation to be computed.
  891. *
  892. * @param tc the text component for which this UI is installed
  893. * @param pt the location in the view to translate. This
  894. * should be in the same coordinate system as the mouse events.
  895. * @return the offset from the start of the document >= 0,
  896. * -1 if not painted
  897. * @see TextUI#viewToModel
  898. */
  899. public int viewToModel(JTextComponent tc, Point pt) {
  900. return viewToModel(tc, pt, discardBias);
  901. }
  902. /**
  903. * Converts the given place in the view coordinate system
  904. * to the nearest representative location in the model.
  905. * The component must have a non-zero positive size for
  906. * this translation to be computed.
  907. *
  908. * @param tc the text component for which this UI is installed
  909. * @param pt the location in the view to translate. This
  910. * should be in the same coordinate system as the mouse events.
  911. * @return the offset from the start of the document >= 0,
  912. * -1 if the component doesn't yet have a positive size.
  913. * @see TextUI#viewToModel
  914. */
  915. public int viewToModel(JTextComponent tc, Point pt,
  916. Position.Bias[] biasReturn) {
  917. int offs = -1;
  918. Document doc = editor.getDocument();
  919. if (doc instanceof AbstractDocument) {
  920. ((AbstractDocument)doc).readLock();
  921. }
  922. try {
  923. Rectangle alloc = getVisibleEditorRect();
  924. if (alloc != null) {
  925. rootView.setSize(alloc.width, alloc.height);
  926. offs = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);
  927. }
  928. } finally {
  929. if (doc instanceof AbstractDocument) {
  930. ((AbstractDocument)doc).readUnlock();
  931. }
  932. }
  933. return offs;
  934. }
  935. /**
  936. * Provides a way to determine the next visually represented model
  937. * location that one might place a caret. Some views may not be visible,
  938. * they might not be in the same order found in the model, or they just
  939. * might not allow access to some of the locations in the model.
  940. *
  941. * @param pos the position to convert >= 0
  942. * @param a the allocated region to render into
  943. * @param direction the direction from the current position that can
  944. * be thought of as the arrow keys typically found on a keyboard.
  945. * This may be SwingConstants.WEST, SwingConstants.EAST,
  946. * SwingConstants.NORTH, or SwingConstants.SOUTH.
  947. * @return the location within the model that best represents the next
  948. * location visual position.
  949. * @exception BadLocationException
  950. * @exception IllegalArgumentException for an invalid direction
  951. */
  952. public int getNextVisualPositionFrom(JTextComponent t, int pos,
  953. Position.Bias b, int direction, Position.Bias[] biasRet)
  954. throws BadLocationException{
  955. Document doc = editor.getDocument();
  956. if (doc instanceof AbstractDocument) {
  957. ((AbstractDocument)doc).readLock();
  958. }
  959. try {
  960. if (painted) {
  961. Rectangle alloc = getVisibleEditorRect();
  962. if (alloc != null) {
  963. rootView.setSize(alloc.width, alloc.height);
  964. }
  965. return rootView.getNextVisualPositionFrom(pos, b, alloc, direction,
  966. biasRet);
  967. }
  968. } finally {
  969. if (doc instanceof AbstractDocument) {
  970. ((AbstractDocument)doc).readUnlock();
  971. }
  972. }
  973. return -1;
  974. }
  975. /**
  976. * Causes the portion of the view responsible for the
  977. * given part of the model to be repainted. Does nothing if
  978. * the view is not currently painted.
  979. *
  980. * @param tc the text component for which this UI is installed
  981. * @param p0 the beginning of the range >= 0
  982. * @param p1 the end of the range >= p0
  983. * @see TextUI#damageRange
  984. */
  985. public void damageRange(JTextComponent tc, int p0, int p1) {
  986. damageRange(tc, p0, p1, Position.Bias.Forward, Position.Bias.Backward);
  987. }
  988. /**
  989. * Causes the portion of the view responsible for the
  990. * given part of the model to be repainted.
  991. *
  992. * @param p0 the beginning of the range >= 0
  993. * @param p1 the end of the range >= p0
  994. */
  995. public void damageRange(JTextComponent t, int p0, int p1,
  996. Position.Bias p0Bias, Position.Bias p1Bias) {
  997. if (painted) {
  998. Rectangle alloc = getVisibleEditorRect();
  999. if (alloc != null) {
  1000. Document doc = t.getDocument();
  1001. if (doc instanceof AbstractDocument) {
  1002. ((AbstractDocument)doc).readLock();
  1003. }
  1004. try {
  1005. rootView.setSize(alloc.width, alloc.height);
  1006. Shape toDamage = rootView.modelToView(p0, p0Bias,
  1007. p1, p1Bias, alloc);
  1008. Rectangle rect = (toDamage instanceof Rectangle) ?
  1009. (Rectangle)toDamage : toDamage.getBounds();
  1010. editor.repaint(rect.x, rect.y, rect.width, rect.height);
  1011. } catch (BadLocationException e) {
  1012. } finally {
  1013. if (doc instanceof AbstractDocument) {
  1014. ((AbstractDocument)doc).readUnlock();
  1015. }
  1016. }
  1017. }
  1018. }
  1019. }
  1020. /**
  1021. * Fetches the EditorKit for the UI.
  1022. *
  1023. * @param tc the text component for which this UI is installed
  1024. * @return the editor capabilities
  1025. * @see TextUI#getEditorKit
  1026. */
  1027. public EditorKit getEditorKit(JTextComponent tc) {
  1028. return defaultKit;
  1029. }
  1030. /**
  1031. * Fetches a View with the allocation of the associated
  1032. * text component (i.e. the root of the hierarchy) that
  1033. * can be traversed to determine how the model is being
  1034. * represented spatially.
  1035. * <p>
  1036. * <font color=red><b>NOTE:</b>The View hierarchy can
  1037. * be traversed from the root view, and other things
  1038. * can be done as well. Things done in this way cannot
  1039. * be protected like simple method calls through the TextUI.
  1040. * Therefore, proper operation in the presence of concurrency
  1041. * must be arranged by any logic that calls this method!
  1042. * </font>
  1043. *
  1044. * @param tc the text component for which this UI is installed
  1045. * @return the view
  1046. * @see TextUI#getRootView
  1047. */
  1048. public View getRootView(JTextComponent tc) {
  1049. return rootView;
  1050. }
  1051. /**
  1052. * Returns the string to be used as the tooltip at the passed in location.
  1053. * This forwards the method onto the root View.
  1054. *
  1055. * @see javax.swing.text.JTextComponent#getToolTipText
  1056. * @see javax.swing.text.View#getToolTipText
  1057. * @since 1.4
  1058. */
  1059. public String getToolTipText(JTextComponent t, Point pt) {
  1060. if (!painted) {
  1061. return null;
  1062. }
  1063. Document doc = editor.getDocument();
  1064. String tt = null;
  1065. Rectangle alloc = getVisibleEditorRect();
  1066. if (alloc != null) {
  1067. if (doc instanceof AbstractDocument) {
  1068. ((AbstractDocument)doc).readLock();
  1069. }
  1070. try {
  1071. tt = rootView.getToolTipText(pt.x, pt.y, alloc);
  1072. } finally {
  1073. if (doc instanceof AbstractDocument) {
  1074. ((AbstractDocument)doc).readUnlock();
  1075. }
  1076. }
  1077. }
  1078. return tt;
  1079. }
  1080. // --- ViewFactory methods ------------------------------
  1081. /**
  1082. * Creates a view for an element.
  1083. * If a subclass wishes to directly implement the factory
  1084. * producing the view(s), it should reimplement this
  1085. * method. By default it simply returns null indicating
  1086. * it is unable to represent the element.
  1087. *
  1088. * @param elem the element
  1089. * @return the view
  1090. */
  1091. public View create(Element elem) {
  1092. return null;
  1093. }
  1094. /**
  1095. * Creates a view for an element.
  1096. * If a subclass wishes to directly implement the factory
  1097. * producing the view(s), it should reimplement this
  1098. * method. By default it simply returns null indicating
  1099. * it is unable to represent the part of the element.
  1100. *
  1101. * @param elem the element
  1102. * @param p0 the starting offset >= 0
  1103. * @param p1 the ending offset >= p0
  1104. * @return the view
  1105. */
  1106. public View create(Element elem, int p0, int p1) {
  1107. return null;
  1108. }
  1109. public static class BasicCaret extends DefaultCaret implements UIResource {}
  1110. public static class BasicHighlighter extends DefaultHighlighter implements UIResource {}
  1111. // ----- member variables ---------------------------------------
  1112. private static final EditorKit defaultKit = new DefaultEditorKit();
  1113. transient JTextComponent editor;
  1114. transient boolean painted;
  1115. transient RootView rootView = new RootView();
  1116. transient UpdateHandler updateHandler = new UpdateHandler();
  1117. private static final TransferHandler defaultTransferHandler = new TextTransferHandler();
  1118. private static DropTargetListener defaultDropTargetListener = null;
  1119. private static final TextDragGestureRecognizer defaultDragRecognizer = new TextDragGestureRecognizer();
  1120. private static final Position.Bias[] discardBias = new Position.Bias[1];
  1121. /**
  1122. * Root view that acts as a gateway between the component
  1123. * and the View hierarchy.
  1124. */
  1125. class RootView extends View {
  1126. RootView() {
  1127. super(null);
  1128. }
  1129. void setView(View v) {
  1130. View oldView = view;
  1131. view = null;
  1132. if (oldView != null) {
  1133. // get rid of back reference so that the old
  1134. // hierarchy can be garbage collected.
  1135. oldView.setParent(null);
  1136. }
  1137. if (v != null) {
  1138. v.setParent(this);
  1139. }
  1140. view = v;
  1141. }
  1142. /**
  1143. * Fetches the attributes to use when rendering. At the root
  1144. * level there are no attributes. If an attribute is resolved
  1145. * up the view hierarchy this is the end of the line.
  1146. */
  1147. public AttributeSet getAttributes() {
  1148. return null;
  1149. }
  1150. /**
  1151. * Determines the preferred span for this view along an axis.
  1152. *
  1153. * @param axis may be either X_AXIS or Y_AXIS
  1154. * @return the span the view would like to be rendered into.
  1155. * Typically the view is told to render into the span
  1156. * that is returned, although there is no guarantee.
  1157. * The parent may choose to resize or break the view.
  1158. */
  1159. public float getPreferredSpan(int axis) {
  1160. if (view != null) {
  1161. return view.getPreferredSpan(axis);
  1162. }
  1163. return 10;
  1164. }
  1165. /**
  1166. * Determines the minimum span for this view along an axis.
  1167. *
  1168. * @param axis may be either X_AXIS or Y_AXIS
  1169. * @return the span the view would like to be rendered into.
  1170. * Typically the view is told to render into the span
  1171. * that is returned, although there is no guarantee.
  1172. * The parent may choose to resize or break the view.
  1173. */
  1174. public float getMinimumSpan(int axis) {
  1175. if (view != null) {
  1176. return view.getMinimumSpan(axis);
  1177. }
  1178. return 10;
  1179. }
  1180. /**
  1181. * Determines the maximum span for this view along an axis.
  1182. *
  1183. * @param axis may be either X_AXIS or Y_AXIS
  1184. * @return the span the view would like to be rendered into.
  1185. * Typically the view is told to render into the span
  1186. * that is returned, although there is no guarantee.
  1187. * The parent may choose to resize or break the view.
  1188. */
  1189. public float getMaximumSpan(int axis) {
  1190. return Integer.MAX_VALUE;
  1191. }
  1192. /**
  1193. * Specifies that a preference has changed.
  1194. * Child views can call this on the parent to indicate that
  1195. * the preference has changed. The root view routes this to
  1196. * invalidate on the hosting component.
  1197. * <p>
  1198. * This can be called on a different thread from the
  1199. * event dispatching thread and is basically unsafe to
  1200. * propagate into the component. To make this safe,
  1201. * the operation is transferred over to the event dispatching
  1202. * thread for completion. It is a design goal that all view
  1203. * methods be safe to call without concern for concurrency,
  1204. * and this behavior helps make that true.
  1205. *
  1206. * @param child the child view
  1207. * @param width true if the width preference has changed
  1208. * @param height true if the height preference has changed
  1209. */
  1210. public void preferenceChanged(View child, boolean width, boolean height) {
  1211. editor.revalidate();
  1212. }
  1213. /**
  1214. * Determines the desired alignment for this view along an axis.
  1215. *
  1216. * @param axis may be either X_AXIS or Y_AXIS
  1217. * @return the desired alignment, where 0.0 indicates the origin
  1218. * and 1.0 the full span away from the origin
  1219. */
  1220. public float getAlignment(int axis) {
  1221. if (view != null) {
  1222. return view.getAlignment(axis);
  1223. }
  1224. return 0;
  1225. }
  1226. /**
  1227. * Renders the view.
  1228. *
  1229. * @param g the graphics context
  1230. * @param allocation the region to render into
  1231. */
  1232. public void paint(Graphics g, Shape allocation) {
  1233. if (view != null) {
  1234. Rectangle alloc = (allocation instanceof Rectangle) ?
  1235. (Rectangle)allocation : allocation.getBounds();
  1236. setSize(alloc.width, alloc.height);
  1237. view.paint(g, allocation);
  1238. }
  1239. }
  1240. /**
  1241. * Sets the view parent.
  1242. *
  1243. * @param parent the parent view
  1244. */
  1245. public void setParent(View parent) {
  1246. throw new Error("Can't set parent on root view");
  1247. }
  1248. /**
  1249. * Returns the number of views in this view. Since
  1250. * this view simply wraps the root of the view hierarchy
  1251. * it has exactly one child.
  1252. *
  1253. * @return the number of views
  1254. * @see #getView
  1255. */
  1256. public int getViewCount() {
  1257. return 1;
  1258. }
  1259. /**
  1260. * Gets the n-th view in this container.
  1261. *
  1262. * @param n the number of the view to get
  1263. * @return the view
  1264. */
  1265. public View getView(int n) {
  1266. return view;
  1267. }
  1268. /**
  1269. * Returns the child view index representing the given position in
  1270. * the model. This is implemented to return the index of the only
  1271. * child.
  1272. *
  1273. * @param pos the position >= 0
  1274. * @return index of the view representing the given position, or
  1275. * -1 if no view represents that position
  1276. * @since 1.3
  1277. */
  1278. public int getViewIndex(int pos, Position.Bias b) {
  1279. return 0;
  1280. }
  1281. /**
  1282. * Fetches the allocation for the given child view.
  1283. * This enables finding out where various views
  1284. * are located, without assuming the views store
  1285. * their location. This returns the given allocation
  1286. * since this view simply acts as a gateway between
  1287. * the view hierarchy and the associated component.
  1288. *
  1289. * @param index the index of the child
  1290. * @param a the allocation to this view.
  1291. * @return the allocation to the child
  1292. */
  1293. public Shape getChildAllocation(int index, Shape a) {
  1294. return a;
  1295. }
  1296. /**
  1297. * Provides a mapping from the document model coordinate space
  1298. * to the coordinate space of the view mapped to it.
  1299. *
  1300. * @param pos the position to convert
  1301. * @param a the allocated region to render into
  1302. * @return the bounding box of the given position
  1303. */
  1304. public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  1305. if (view != null) {
  1306. return view.modelToView(pos, a, b);
  1307. }
  1308. return null;
  1309. }
  1310. /**
  1311. * Provides a mapping from the document model coordinate space
  1312. * to the coordinate space of the view mapped to it.
  1313. *
  1314. * @param p0 the position to convert >= 0
  1315. * @param b0 the bias toward the previous character or the
  1316. * next character represented by p0, in case the
  1317. * position is a boundary of two views.
  1318. * @param p1 the position to convert >= 0
  1319. * @param b1 the bias toward the previous character or the
  1320. * next character represented by p1, in case the
  1321. * position is a boundary of two views.
  1322. * @param a the allocated region to render into
  1323. * @return the bounding box of the given position is returned
  1324. * @exception BadLocationException if the given position does
  1325. * not represent a valid location in the associated document
  1326. * @exception IllegalArgumentException for an invalid bias argument
  1327. * @see View#viewToModel
  1328. */
  1329. public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a) throws BadLocationException {
  1330. if (view != null) {
  1331. return view.modelToView(p0, b0, p1, b1, a);
  1332. }
  1333. return null;
  1334. }
  1335. /**
  1336. * Provides a mapping from the view coordinate space to the logical
  1337. * coordinate space of the model.
  1338. *
  1339. * @param x x coordinate of the view location to convert
  1340. * @param y y coordinate of the view location to convert
  1341. * @param a the allocated region to render into
  1342. * @return the location within the model that best represents the
  1343. * given point in the view
  1344. */
  1345. public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
  1346. if (view != null) {
  1347. int retValue = view.viewToModel(x, y, a, bias);
  1348. return retValue;
  1349. }
  1350. return -1;
  1351. }
  1352. /**
  1353. * Provides a way to determine the next visually represented model
  1354. * location that one might place a caret. Some views may not be visible,
  1355. * they might not be in the same order found in the model, or they just
  1356. * might not allow access to some of the locations in the model.
  1357. *
  1358. * @param pos the position to convert >= 0
  1359. * @param a the allocated region to render into
  1360. * @param direction the direction from the current position that can
  1361. * be thought of as the arrow keys typically found on a keyboard.
  1362. * This may be SwingConstants.WEST, SwingConstants.EAST,
  1363. * SwingConstants.NORTH, or SwingConstants.SOUTH.
  1364. * @return the location within the model that best represents the next
  1365. * location visual position.
  1366. * @exception BadLocationException
  1367. * @exception IllegalArgumentException for an invalid direction
  1368. */
  1369. public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
  1370. int direction,
  1371. Position.Bias[] biasRet)
  1372. throws BadLocationException {
  1373. if( view != null ) {
  1374. int nextPos = view.getNextVisualPositionFrom(pos, b, a,
  1375. direction, biasRet);
  1376. if(nextPos != -1) {
  1377. pos = nextPos;
  1378. }
  1379. else {
  1380. biasRet[0] = b;
  1381. }
  1382. }
  1383. return pos;
  1384. }
  1385. /**
  1386. * Gives notification that something was inserted into the document
  1387. * in a location that this view is responsible for.
  1388. *
  1389. * @param e the change information from the associated document
  1390. * @param a the current allocation of the view
  1391. * @param f the factory to use to rebuild if the view has children
  1392. */
  1393. public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  1394. if (view != null) {
  1395. view.insertUpdate(e, a, f);
  1396. }
  1397. }
  1398. /**
  1399. * Gives notification that something was removed from the document
  1400. * in a location that this view is responsible for.
  1401. *
  1402. * @param e the change information from the associated document
  1403. * @param a the current allocation of the view
  1404. * @param f the factory to use to rebuild if the view has children
  1405. */
  1406. public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  1407. if (view != null) {
  1408. view.removeUpdate(e, a, f);
  1409. }
  1410. }
  1411. /**
  1412. * Gives notification from the document that attributes were changed
  1413. * in a location that this view is responsible for.
  1414. *
  1415. * @param e the change information from the associated document
  1416. * @param a the current allocation of the view
  1417. * @param f the factory to use to rebuild if the view has children
  1418. */
  1419. public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  1420. if (view != null) {
  1421. view.changedUpdate(e, a, f);
  1422. }
  1423. }
  1424. /**
  1425. * Returns the document model underlying the view.
  1426. *
  1427. * @return the model
  1428. */
  1429. public Document getDocument() {
  1430. return editor.getDocument();
  1431. }
  1432. /**
  1433. * Returns the starting offset into the model for this view.
  1434. *
  1435. * @return the starting offset
  1436. */
  1437. public int getStartOffset() {
  1438. if (view != null) {
  1439. return view.getStartOffset();
  1440. }
  1441. return getElement().getStartOffset();
  1442. }
  1443. /**
  1444. * Returns the ending offset into the model for this view.
  1445. *
  1446. * @return the ending offset
  1447. */
  1448. public int getEndOffset() {
  1449. if (view != null) {
  1450. return view.getEndOffset();
  1451. }
  1452. return getElement().getEndOffset();
  1453. }
  1454. /**
  1455. * Gets the element that this view is mapped to.
  1456. *
  1457. * @return the view
  1458. */
  1459. public Element getElement() {
  1460. if (view != null) {
  1461. return view.getElement();
  1462. }
  1463. return editor.getDocument().getDefaultRootElement();
  1464. }
  1465. /**
  1466. * Breaks this view on the given axis at the given length.
  1467. *
  1468. * @param axis may be either X_AXIS or Y_AXIS
  1469. * @param len specifies where a break is desired in the span
  1470. * @param the current allocation of the view
  1471. * @return the fragment of the view that represents the given span
  1472. * if the view can be broken, otherwise null
  1473. */
  1474. public View breakView(int axis, float len, Shape a) {
  1475. throw new Error("Can't break root view");
  1476. }
  1477. /**
  1478. * Determines the resizability of the view along the
  1479. * given axis. A value of 0 or less is not resizable.
  1480. *
  1481. * @param axis may be either X_AXIS or Y_AXIS
  1482. * @return the weight
  1483. */
  1484. public int getResizeWeight(int axis) {
  1485. if (view != null) {
  1486. return view.getResizeWeight(axis);
  1487. }
  1488. return 0;
  1489. }
  1490. /**
  1491. * Sets the view size.
  1492. *
  1493. * @param width the width
  1494. * @param height the height
  1495. */
  1496. public void setSize(float width, float height) {
  1497. if (view != null) {
  1498. view.setSize(width, height);
  1499. }
  1500. }
  1501. /**
  1502. * Fetches the container hosting the view. This is useful for
  1503. * things like scheduling a repaint, finding out the host
  1504. * components font, etc. The default implementation
  1505. * of this is to forward the query to the parent view.
  1506. *
  1507. * @return the container
  1508. */
  1509. public Container getContainer() {
  1510. return editor;
  1511. }
  1512. /**
  1513. * Fetches the factory to be used for building the
  1514. * various view fragments that make up the view that
  1515. * represents the model. This is what determines
  1516. * how the model will be represented. This is implemented
  1517. * to fetch the factory provided by the associated
  1518. * EditorKit unless that is null, in which case this
  1519. * simply returns the BasicTextUI itself which allows
  1520. * subclasses to implement a simple factory directly without
  1521. * creating extra objects.
  1522. *
  1523. * @return the factory
  1524. */
  1525. public ViewFactory getViewFactory() {
  1526. EditorKit kit = getEditorKit(editor);
  1527. ViewFactory f = kit.getViewFactory();
  1528. if (f != null) {
  1529. return f;
  1530. }
  1531. return BasicTextUI.this;
  1532. }
  1533. private View view;
  1534. }
  1535. /**
  1536. * Handles updates from various places. If the model is changed,
  1537. * this class unregisters as a listener to the old model and
  1538. * registers with the new model. If the document model changes,
  1539. * the change is forwarded to the root view. If the focus
  1540. * accelerator changes, a new keystroke is registered to request
  1541. * focus.
  1542. */
  1543. class UpdateHandler implements PropertyChangeListener, DocumentListener, LayoutManager2, UIResource {
  1544. // --- PropertyChangeListener methods -----------------------
  1545. /**
  1546. * This method gets called when a bound property is changed.
  1547. * We are looking for document changes on the editor.
  1548. */
  1549. public final void propertyChange(PropertyChangeEvent evt) {
  1550. Object oldValue = evt.getOldValue();
  1551. Object newValue = evt.getNewValue();
  1552. String propertyName = evt.getPropertyName();
  1553. if ((oldValue instanceof Document) || (newValue instanceof Document)) {
  1554. if (oldValue != null) {
  1555. ((Document)oldValue).removeDocumentListener(this);
  1556. i18nView = false;
  1557. }
  1558. if (newValue != null) {
  1559. ((Document)newValue).addDocumentListener(this);
  1560. if ("document" == propertyName) {
  1561. setView(null);
  1562. BasicTextUI.this.propertyChange(evt);
  1563. modelChanged();
  1564. return;
  1565. }
  1566. }
  1567. modelChanged();
  1568. }
  1569. if ("focusAccelerator" == propertyName) {
  1570. updateFocusAcceleratorBinding(true);
  1571. } else if ("componentOrientation" == propertyName) {
  1572. // Changes in ComponentOrientation require the views to be
  1573. // rebuilt.
  1574. modelChanged();
  1575. } else if ("font" == propertyName) {
  1576. modelChanged();
  1577. } else if ("transferHandler" == propertyName) {
  1578. DropTarget dropTarget = editor.getDropTarget();
  1579. if (dropTarget instanceof UIResource) {
  1580. if (defaultDropTargetListener == null) {
  1581. defaultDropTargetListener = new TextDropTargetListener();
  1582. }
  1583. try {
  1584. dropTarget.addDropTargetListener(defaultDropTargetListener);
  1585. } catch (TooManyListenersException tmle) {
  1586. // should not happen... swing drop target is multicast
  1587. }
  1588. }
  1589. } else if ("editable" == propertyName) {
  1590. modelChanged();
  1591. }
  1592. BasicTextUI.this.propertyChange(evt);
  1593. }
  1594. // --- DocumentListener methods -----------------------
  1595. /**
  1596. * The insert notification. Gets sent to the root of the view structure
  1597. * that represents the portion of the model being represented by the
  1598. * editor. The factory is added as an argument to the update so that
  1599. * the views can update themselves in a dynamic (not hardcoded) way.
  1600. *
  1601. * @param e The change notification from the currently associated
  1602. * document.
  1603. * @see DocumentListener#insertUpdate
  1604. */
  1605. public final void insertUpdate(DocumentEvent e) {
  1606. Document doc = e.getDocument();
  1607. Object o = doc.getProperty("i18n");
  1608. if (o instanceof Boolean) {
  1609. Boolean i18nFlag = (Boolean) o;
  1610. if (i18nFlag.booleanValue() != i18nView) {
  1611. // i18n flag changed, rebuild the view
  1612. i18nView = i18nFlag.booleanValue();
  1613. modelChanged();
  1614. return;
  1615. }
  1616. }
  1617. // normal insert update
  1618. Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
  1619. rootView.insertUpdate(e, alloc, rootView.getViewFactory());
  1620. }
  1621. /**
  1622. * The remove notification. Gets sent to the root of the view structure
  1623. * that represents the portion of the model being represented by the
  1624. * editor. The factory is added as an argument to the update so that
  1625. * the views can update themselves in a dynamic (not hardcoded) way.
  1626. *
  1627. * @param e The change notification from the currently associated
  1628. * document.
  1629. * @see DocumentListener#removeUpdate
  1630. */
  1631. public final void removeUpdate(DocumentEvent e) {
  1632. Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
  1633. rootView.removeUpdate(e, alloc, rootView.getViewFactory());
  1634. }
  1635. /**
  1636. * The change notification. Gets sent to the root of the view structure
  1637. * that represents the portion of the model being represented by the
  1638. * editor. The factory is added as an argument to the update so that
  1639. * the views can update themselves in a dynamic (not hardcoded) way.
  1640. *
  1641. * @param e The change notification from the currently associated
  1642. * document.
  1643. * @see DocumentListener#changeUpdate
  1644. */
  1645. public final void changedUpdate(DocumentEvent e) {
  1646. Rectangle alloc = (painted) ? getVisibleEditorRect() : null;
  1647. rootView.changedUpdate(e, alloc, rootView.getViewFactory());
  1648. }
  1649. // --- LayoutManager2 methods --------------------------------
  1650. /**
  1651. * Adds the specified component with the specified name to
  1652. * the layout.
  1653. * @param name the component name
  1654. * @param comp the component to be added
  1655. */
  1656. public void addLayoutComponent(String name, Component comp) {
  1657. // not supported
  1658. }
  1659. /**
  1660. * Removes the specified component from the layout.
  1661. * @param comp the component to be removed
  1662. */
  1663. public void removeLayoutComponent(Component comp) {
  1664. if (constraints != null) {
  1665. // remove the constraint record
  1666. constraints.remove(comp);
  1667. }
  1668. }
  1669. /**
  1670. * Calculates the preferred size dimensions for the specified
  1671. * panel given the components in the specified parent container.
  1672. * @param parent the component to be laid out
  1673. *
  1674. * @see #minimumLayoutSize
  1675. */
  1676. public Dimension preferredLayoutSize(Container parent) {
  1677. // should not be called (JComponent uses UI instead)
  1678. return null;
  1679. }
  1680. /**
  1681. * Calculates the minimum size dimensions for the specified
  1682. * panel given the components in the specified parent container.
  1683. * @param parent the component to be laid out
  1684. * @see #preferredLayoutSize
  1685. */
  1686. public Dimension minimumLayoutSize(Container parent) {
  1687. // should not be called (JComponent uses UI instead)
  1688. return null;
  1689. }
  1690. /**
  1691. * Lays out the container in the specified panel. This is
  1692. * implemented to position all components that were added
  1693. * with a View object as a constraint. The current allocation
  1694. * of the associated View is used as the location of the
  1695. * component.
  1696. * <p>
  1697. * A read-lock is acquired on the document to prevent the
  1698. * view tree from being modified while the layout process
  1699. * is active.
  1700. *
  1701. * @param parent the component which needs to be laid out
  1702. */
  1703. public void layoutContainer(Container parent) {
  1704. if ((constraints != null) && (! constraints.isEmpty())) {
  1705. Rectangle alloc = getVisibleEditorRect();
  1706. if (alloc != null) {
  1707. Document doc = editor.getDocument();
  1708. if (doc instanceof AbstractDocument) {
  1709. ((AbstractDocument)doc).readLock();
  1710. }
  1711. try {
  1712. rootView.setSize(alloc.width, alloc.height);
  1713. Enumeration components = constraints.keys();
  1714. while (components.hasMoreElements()) {
  1715. Component comp = (Component) components.nextElement();
  1716. View v = (View) constraints.get(comp);
  1717. Shape ca = calculateViewPosition(alloc, v);
  1718. if (ca != null) {
  1719. Rectangle compAlloc = (ca instanceof Rectangle) ?
  1720. (Rectangle) ca : ca.getBounds();
  1721. comp.setBounds(compAlloc);
  1722. }
  1723. }
  1724. } finally {
  1725. if (doc instanceof AbstractDocument) {
  1726. ((AbstractDocument)doc).readUnlock();
  1727. }
  1728. }
  1729. }
  1730. }
  1731. }
  1732. /**
  1733. * Find the Shape representing the given view.
  1734. */
  1735. Shape calculateViewPosition(Shape alloc, View v) {
  1736. int pos = v.getStartOffset();
  1737. View child = null;
  1738. for (View parent = rootView; (parent != null) && (parent != v); parent = child) {
  1739. int index = parent.getViewIndex(pos, Position.Bias.Forward);
  1740. alloc = parent.getChildAllocation(index, alloc);
  1741. child = parent.getView(index);
  1742. }
  1743. return (child != null) ? alloc : null;
  1744. }
  1745. /**
  1746. * Adds the specified component to the layout, using the specified
  1747. * constraint object. We only store those components that were added
  1748. * with a constraint that is of type View.
  1749. *
  1750. * @param comp the component to be added
  1751. * @param constraint where/how the component is added to the layout.
  1752. */
  1753. public void addLayoutComponent(Component comp, Object constraint) {
  1754. if (constraint instanceof View) {
  1755. if (constraints == null) {
  1756. constraints = new Hashtable(7);
  1757. }
  1758. constraints.put(comp, constraint);
  1759. }
  1760. }
  1761. /**
  1762. * Returns the maximum size of this component.
  1763. * @see java.awt.Component#getMinimumSize()
  1764. * @see java.awt.Component#getPreferredSize()
  1765. * @see LayoutManager
  1766. */
  1767. public Dimension maximumLayoutSize(Container target) {
  1768. // should not be called (JComponent uses UI instead)
  1769. return null;
  1770. }
  1771. /**
  1772. * Returns the alignment along the x axis. This specifies how
  1773. * the component would like to be aligned relative to other
  1774. * components. The value should be a number between 0 and 1
  1775. * where 0 represents alignment along the origin, 1 is aligned
  1776. * the furthest away from the origin, 0.5 is centered, etc.
  1777. */
  1778. public float getLayoutAlignmentX(Container target) {
  1779. return 0.5f;
  1780. }
  1781. /**
  1782. * Returns the alignment along the y axis. This specifies how
  1783. * the component would like to be aligned relative to other
  1784. * components. The value should be a number between 0 and 1
  1785. * where 0 represents alignment along the origin, 1 is aligned
  1786. * the furthest away from the origin, 0.5 is centered, etc.
  1787. */
  1788. public float getLayoutAlignmentY(Container target) {
  1789. return 0.5f;
  1790. }
  1791. /**
  1792. * Invalidates the layout, indicating that if the layout manager
  1793. * has cached information it should be discarded.
  1794. */
  1795. public void invalidateLayout(Container target) {
  1796. }
  1797. /**
  1798. * The "layout constraints" for the LayoutManager2 implementation.
  1799. * These are View objects for those components that are represented
  1800. * by a View in the View tree.
  1801. */
  1802. private Hashtable constraints;
  1803. private boolean i18nView = false;
  1804. }
  1805. /**
  1806. * Wrapper for text actions to return isEnabled false in case editor is non editable
  1807. */
  1808. class TextActionWrapper extends TextAction {
  1809. public TextActionWrapper(TextAction action) {
  1810. super((String)action.getValue(Action.NAME));
  1811. this.action = action;
  1812. }
  1813. /**
  1814. * The operation to perform when this action is triggered.
  1815. *
  1816. * @param e the action event
  1817. */
  1818. public void actionPerformed(ActionEvent e) {
  1819. action.actionPerformed(e);
  1820. }
  1821. public boolean isEnabled() {
  1822. return (editor == null || editor.isEditable()) ? action.isEnabled() : false;
  1823. }
  1824. TextAction action = null;
  1825. }
  1826. /**
  1827. * Registered in the ActionMap.
  1828. */
  1829. class FocusAction extends AbstractAction {
  1830. public void actionPerformed(ActionEvent e) {
  1831. editor.requestFocus();
  1832. }
  1833. public boolean isEnabled() {
  1834. return editor.isEditable();
  1835. }
  1836. }
  1837. /**
  1838. * Drag gesture recognizer for text components.
  1839. */
  1840. static class TextDragGestureRecognizer extends BasicDragGestureRecognizer {
  1841. /**
  1842. * Determines if the following are true:
  1843. * <ul>
  1844. * <li>the press event is located over a selection
  1845. * <li>the dragEnabled property is true
  1846. * <li>A TranferHandler is installed
  1847. * </ul>
  1848. * <p>
  1849. * This is implemented to check for a TransferHandler.
  1850. * Subclasses should perform the remaining conditions.
  1851. */
  1852. protected boolean isDragPossible(MouseEvent e) {
  1853. if (super.isDragPossible(e)) {
  1854. JTextComponent c = (JTextComponent) this.getComponent(e);
  1855. if (c.getDragEnabled()) {
  1856. Caret caret = c.getCaret();
  1857. int dot = caret.getDot();
  1858. int mark = caret.getMark();
  1859. if (dot != mark) {
  1860. Point p = new Point(e.getX(), e.getY());
  1861. int pos = c.viewToModel(p);
  1862. int p0 = Math.min(dot, mark);
  1863. int p1 = Math.max(dot, mark);
  1864. if ((pos >= p0) && (pos < p1)) {
  1865. return true;
  1866. }
  1867. }
  1868. }
  1869. }
  1870. return false;
  1871. }
  1872. }
  1873. /**
  1874. * A DropTargetListener to extend the default Swing handling of drop operations
  1875. * by moving the caret to the nearest location to the mouse pointer.
  1876. */
  1877. static class TextDropTargetListener extends BasicDropTargetListener {
  1878. /**
  1879. * called to save the state of a component in case it needs to
  1880. * be restored because a drop is not performed.
  1881. */
  1882. protected void saveComponentState(JComponent comp) {
  1883. JTextComponent c = (JTextComponent) comp;
  1884. Caret caret = c.getCaret();
  1885. dot = caret.getDot();
  1886. mark = caret.getMark();
  1887. visible = caret instanceof DefaultCaret ?
  1888. ((DefaultCaret)caret).isActive() :
  1889. caret.isVisible();
  1890. caret.setVisible(true);
  1891. }
  1892. /**
  1893. * called to restore the state of a component
  1894. * because a drop was not performed.
  1895. */
  1896. protected void restoreComponentState(JComponent comp) {
  1897. JTextComponent c = (JTextComponent) comp;
  1898. Caret caret = c.getCaret();
  1899. caret.setDot(mark);
  1900. caret.moveDot(dot);
  1901. caret.setVisible(visible);
  1902. }
  1903. /**
  1904. * called to restore the state of a component
  1905. * because a drop was performed.
  1906. */
  1907. protected void restoreComponentStateForDrop(JComponent comp) {
  1908. JTextComponent c = (JTextComponent) comp;
  1909. Caret caret = c.getCaret();
  1910. caret.setVisible(visible);
  1911. }
  1912. /**
  1913. * called to set the insertion location to match the current
  1914. * mouse pointer coordinates.
  1915. */
  1916. protected void updateInsertionLocation(JComponent comp, Point p) {
  1917. JTextComponent c = (JTextComponent) comp;
  1918. c.setCaretPosition(c.viewToModel(p));
  1919. }
  1920. int dot;
  1921. int mark;
  1922. boolean visible;
  1923. }
  1924. static class TextTransferHandler extends TransferHandler implements UIResource {
  1925. private JTextComponent exportComp;
  1926. private boolean shouldRemove;
  1927. private int p0;
  1928. private int p1;
  1929. /**
  1930. * Try to find a flavor that can be used to import a Transferable.
  1931. * The set of usable flavors are tried in the following order:
  1932. * <ol>
  1933. * <li>First, an attempt is made to find a flavor matching the content type
  1934. * of the EditorKit for the component.
  1935. * <li>Second, an attempt to find a text/plain flavor is made.
  1936. * <li>Third, an attempt to find a flavor representing a String reference
  1937. * in the same VM is made.
  1938. * <li>Lastly, DataFlavor.stringFlavor is searched for.
  1939. * </ol>
  1940. */
  1941. protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) {
  1942. DataFlavor plainFlavor = null;
  1943. DataFlavor refFlavor = null;
  1944. DataFlavor stringFlavor = null;
  1945. if (c instanceof JEditorPane) {
  1946. for (int i = 0; i < flavors.length; i++) {
  1947. String mime = flavors[i].getMimeType();
  1948. if (mime.startsWith(((JEditorPane)c).getEditorKit().getContentType())) {
  1949. return flavors[i];
  1950. } else if (plainFlavor == null && mime.startsWith("text/plain")) {
  1951. plainFlavor = flavors[i];
  1952. } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")
  1953. && flavors[i].getRepresentationClass() == java.lang.String.class) {
  1954. refFlavor = flavors[i];
  1955. } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
  1956. stringFlavor = flavors[i];
  1957. }
  1958. }
  1959. if (plainFlavor != null) {
  1960. return plainFlavor;
  1961. } else if (refFlavor != null) {
  1962. return refFlavor;
  1963. } else if (stringFlavor != null) {
  1964. return stringFlavor;
  1965. }
  1966. return null;
  1967. }
  1968. for (int i = 0; i < flavors.length; i++) {
  1969. String mime = flavors[i].getMimeType();
  1970. if (mime.startsWith("text/plain")) {
  1971. return flavors[i];
  1972. } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")
  1973. && flavors[i].getRepresentationClass() == java.lang.String.class) {
  1974. refFlavor = flavors[i];
  1975. } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
  1976. stringFlavor = flavors[i];
  1977. }
  1978. }
  1979. if (refFlavor != null) {
  1980. return refFlavor;
  1981. } else if (stringFlavor != null) {
  1982. return stringFlavor;
  1983. }
  1984. return null;
  1985. }
  1986. /**
  1987. * Import the given stream data into the text component.
  1988. */
  1989. protected void handleReaderImport(Reader in, JTextComponent c, boolean useRead)
  1990. throws BadLocationException, IOException {
  1991. if (useRead) {
  1992. int startPosition = c.getSelectionStart();
  1993. int endPosition = c.getSelectionEnd();
  1994. int length = endPosition - startPosition;
  1995. EditorKit kit = c.getUI().getEditorKit(c);
  1996. Document doc = c.getDocument();
  1997. if (length > 0) {
  1998. doc.remove(startPosition, length);
  1999. }
  2000. kit.read(in, doc, startPosition);
  2001. } else {
  2002. char[] buff = new char[1024];
  2003. int nch;
  2004. boolean lastWasCR = false;
  2005. int last;
  2006. StringBuffer sbuff = null;
  2007. // Read in a block at a time, mapping \r\n to \n, as well as single
  2008. // \r to \n.
  2009. while ((nch = in.read(buff, 0, buff.length)) != -1) {
  2010. if (sbuff == null) {
  2011. sbuff = new StringBuffer(nch);
  2012. }
  2013. last = 0;
  2014. for(int counter = 0; counter < nch; counter++) {
  2015. switch(buff[counter]) {
  2016. case '\r':
  2017. if (lastWasCR) {
  2018. if (counter == 0) {
  2019. sbuff.append('\n');
  2020. } else {
  2021. buff[counter - 1] = '\n';
  2022. }
  2023. } else {
  2024. lastWasCR = true;
  2025. }
  2026. break;
  2027. case '\n':
  2028. if (lastWasCR) {
  2029. if (counter > (last + 1)) {
  2030. sbuff.append(buff, last, counter - last - 1);
  2031. }
  2032. // else nothing to do, can skip \r, next write will
  2033. // write \n
  2034. lastWasCR = false;
  2035. last = counter;
  2036. }
  2037. break;
  2038. default:
  2039. if (lastWasCR) {
  2040. if (counter == 0) {
  2041. sbuff.append('\n');
  2042. } else {
  2043. buff[counter - 1] = '\n';
  2044. }
  2045. lastWasCR = false;
  2046. }
  2047. break;
  2048. }
  2049. }
  2050. if (last < nch) {
  2051. if (lastWasCR) {
  2052. if (last < (nch - 1)) {
  2053. sbuff.append(buff, last, nch - last - 1);
  2054. }
  2055. } else {
  2056. sbuff.append(buff, last, nch - last);
  2057. }
  2058. }
  2059. }
  2060. if (lastWasCR) {
  2061. sbuff.append('\n');
  2062. }
  2063. c.replaceSelection(sbuff != null ? sbuff.toString() : "");
  2064. }
  2065. }
  2066. // --- TransferHandler methods ------------------------------------
  2067. /**
  2068. * This is the type of transfer actions supported by the source. Some models are
  2069. * not mutable, so a transfer operation of COPY only should
  2070. * be advertised in that case.
  2071. *
  2072. * @param c The component holding the data to be transfered. This
  2073. * argument is provided to enable sharing of TransferHandlers by
  2074. * multiple components.
  2075. * @return This is implemented to return NONE if the component is a JPasswordField
  2076. * since exporting data via user gestures is not allowed. If the text component is
  2077. * editable, COPY_OR_MOVE is returned, otherwise just COPY is allowed.
  2078. */
  2079. public int getSourceActions(JComponent c) {
  2080. if (c instanceof JPasswordField &&
  2081. c.getClientProperty("JPasswordField.cutCopyAllowed") !=
  2082. Boolean.TRUE) {
  2083. return NONE;
  2084. }
  2085. return ((JTextComponent)c).isEditable() ? COPY_OR_MOVE : COPY;
  2086. }
  2087. /**
  2088. * Create a Transferable to use as the source for a data transfer.
  2089. *
  2090. * @param comp The component holding the data to be transfered. This
  2091. * argument is provided to enable sharing of TransferHandlers by
  2092. * multiple components.
  2093. * @return The representation of the data to be transfered.
  2094. *
  2095. */
  2096. protected Transferable createTransferable(JComponent comp) {
  2097. exportComp = (JTextComponent)comp;
  2098. shouldRemove = true;
  2099. p0 = exportComp.getSelectionStart();
  2100. p1 = exportComp.getSelectionEnd();
  2101. return (p0 != p1) ? (new TextTransferable(exportComp, p0, p1)) : null;
  2102. }
  2103. /**
  2104. * This method is called after data has been exported. This method should remove
  2105. * the data that was transfered if the action was MOVE.
  2106. *
  2107. * @param source The component that was the source of the data.
  2108. * @param data The data that was transferred or possibly null
  2109. * if the action is <code>NONE</code>.
  2110. * @param action The actual action that was performed.
  2111. */
  2112. protected void exportDone(JComponent source, Transferable data, int action) {
  2113. // only remove the text if shouldRemove has not been set to
  2114. // false by importData and only if the action is a move
  2115. if (shouldRemove && action == MOVE) {
  2116. TextTransferable t = (TextTransferable)data;
  2117. t.removeText();
  2118. }
  2119. exportComp = null;
  2120. }
  2121. /**
  2122. * This method causes a transfer to a component from a clipboard or a
  2123. * DND drop operation. The Transferable represents the data to be
  2124. * imported into the component.
  2125. *
  2126. * @param comp The component to receive the transfer. This
  2127. * argument is provided to enable sharing of TransferHandlers by
  2128. * multiple components.
  2129. * @param t The data to import
  2130. * @return true if the data was inserted into the component, false otherwise.
  2131. */
  2132. public boolean importData(JComponent comp, Transferable t) {
  2133. JTextComponent c = (JTextComponent)comp;
  2134. // if we are importing to the same component that we exported from
  2135. // then don't actually do anything if the drop location is inside
  2136. // the drag location and set shouldRemove to false so that exportDone
  2137. // knows not to remove any data
  2138. if (c == exportComp && c.getCaretPosition() >= p0 && c.getCaretPosition() <= p1) {
  2139. shouldRemove = false;
  2140. return true;
  2141. }
  2142. boolean imported = false;
  2143. DataFlavor importFlavor = getImportFlavor(t.getTransferDataFlavors(), c);
  2144. if (importFlavor != null) {
  2145. try {
  2146. boolean useRead = false;
  2147. if (comp instanceof JEditorPane) {
  2148. JEditorPane ep = (JEditorPane)comp;
  2149. if (!ep.getContentType().startsWith("text/plain") &&
  2150. importFlavor.getMimeType().startsWith(ep.getContentType())) {
  2151. useRead = true;
  2152. }
  2153. }
  2154. InputContext ic = c.getInputContext();
  2155. if (ic != null) {
  2156. ic.endComposition();
  2157. }
  2158. Reader r = importFlavor.getReaderForText(t);
  2159. handleReaderImport(r, c, useRead);
  2160. imported = true;
  2161. } catch (UnsupportedFlavorException ufe) {
  2162. } catch (BadLocationException ble) {
  2163. } catch (IOException ioe) {
  2164. }
  2165. }
  2166. return imported;
  2167. }
  2168. /**
  2169. * This method indicates if a component would accept an import of the given
  2170. * set of data flavors prior to actually attempting to import it.
  2171. *
  2172. * @param comp The component to receive the transfer. This
  2173. * argument is provided to enable sharing of TransferHandlers by
  2174. * multiple components.
  2175. * @param flavors The data formats available
  2176. * @return true if the data can be inserted into the component, false otherwise.
  2177. */
  2178. public boolean canImport(JComponent comp, DataFlavor[] flavors) {
  2179. JTextComponent c = (JTextComponent)comp;
  2180. if (!(c.isEditable() && c.isEnabled())) {
  2181. return false;
  2182. }
  2183. return (getImportFlavor(flavors, c) != null);
  2184. }
  2185. /**
  2186. * A possible implementation of the Transferable interface
  2187. * for text components. For a JEditorPane with a rich set
  2188. * of EditorKit implementations, conversions could be made
  2189. * giving a wider set of formats. This is implemented to
  2190. * offer up only the active content type and text/plain
  2191. * (if that is not the active format) since that can be
  2192. * extracted from other formats.
  2193. */
  2194. static class TextTransferable extends BasicTransferable {
  2195. TextTransferable(JTextComponent c, int start, int end) {
  2196. super(null, null);
  2197. this.c = c;
  2198. Document doc = c.getDocument();
  2199. try {
  2200. p0 = doc.createPosition(start);
  2201. p1 = doc.createPosition(end);
  2202. plainData = c.getSelectedText();
  2203. if (c instanceof JEditorPane) {
  2204. JEditorPane ep = (JEditorPane)c;
  2205. mimeType = ep.getContentType();
  2206. if (mimeType.startsWith("text/plain")) {
  2207. return;
  2208. }
  2209. StringWriter sw = new StringWriter(p1.getOffset() - p0.getOffset());
  2210. ep.getEditorKit().write(sw, doc, p0.getOffset(), p1.getOffset() - p0.getOffset());
  2211. if (mimeType.startsWith("text/html")) {
  2212. htmlData = sw.toString();
  2213. } else {
  2214. richText = sw.toString();
  2215. }
  2216. }
  2217. } catch (BadLocationException ble) {
  2218. } catch (IOException ioe) {
  2219. }
  2220. }
  2221. void removeText() {
  2222. if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) {
  2223. try {
  2224. Document doc = c.getDocument();
  2225. doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset());
  2226. } catch (BadLocationException e) {
  2227. }
  2228. }
  2229. }
  2230. // ---- EditorKit other than plain or HTML text -----------------------
  2231. /**
  2232. * If the EditorKit is not for text/plain or text/html, that format
  2233. * is supported through the "richer flavors" part of BasicTransferable.
  2234. */
  2235. protected DataFlavor[] getRicherFlavors() {
  2236. if (richText == null) {
  2237. return null;
  2238. }
  2239. try {
  2240. DataFlavor[] flavors = new DataFlavor[3];
  2241. flavors[0] = new DataFlavor(mimeType + ";class=java.lang.String");
  2242. flavors[1] = new DataFlavor(mimeType + ";class=java.io.Reader");
  2243. flavors[2] = new DataFlavor(mimeType + ";class=java.io.InputStream;charset=unicode");
  2244. return flavors;
  2245. } catch (ClassNotFoundException cle) {
  2246. // fall through to unsupported (should not happen)
  2247. }
  2248. return null;
  2249. }
  2250. /**
  2251. * The only richer format supported is the file list flavor
  2252. */
  2253. protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException {
  2254. if (richText == null) {
  2255. return null;
  2256. }
  2257. if (String.class.equals(flavor.getRepresentationClass())) {
  2258. return richText;
  2259. } else if (Reader.class.equals(flavor.getRepresentationClass())) {
  2260. return new StringReader(richText);
  2261. } else if (InputStream.class.equals(flavor.getRepresentationClass())) {
  2262. return new StringBufferInputStream(richText);
  2263. }
  2264. throw new UnsupportedFlavorException(flavor);
  2265. }
  2266. Position p0;
  2267. Position p1;
  2268. String mimeType;
  2269. String richText;
  2270. JTextComponent c;
  2271. }
  2272. }
  2273. }