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