1. /*
  2. * @(#)JTextPane.java 1.90 03/12/19
  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;
  8. import java.awt.*;
  9. import java.awt.event.ActionEvent;
  10. import java.io.ObjectOutputStream;
  11. import java.io.ObjectInputStream;
  12. import java.io.IOException;
  13. import javax.swing.text.*;
  14. import javax.swing.event.*;
  15. import javax.swing.plaf.*;
  16. /**
  17. * A text component that can be marked up with attributes that are
  18. * represented graphically.
  19. * You can find how-to information and examples of using text panes in
  20. * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/text.html">Using Text Components</a>,
  21. * a section in <em>The Java Tutorial.</em>
  22. *
  23. * <p>
  24. * This component models paragraphs
  25. * that are composed of runs of character level attributes. Each
  26. * paragraph may have a logical style attached to it which contains
  27. * the default attributes to use if not overridden by attributes set
  28. * on the paragraph or character run. Components and images may
  29. * be embedded in the flow of text.
  30. * <p>
  31. * <dt><b><font size=+1>Newlines</font></b>
  32. * <dd>
  33. * For a discussion on how newlines are handled, see
  34. * <a href="text/DefaultEditorKit.html">DefaultEditorKit</a>.
  35. * </dl>
  36. *
  37. * <p>
  38. * <strong>Warning:</strong>
  39. * Serialized objects of this class will not be compatible with
  40. * future Swing releases. The current serialization support is
  41. * appropriate for short term storage or RMI between applications running
  42. * the same version of Swing. As of 1.4, support for long term storage
  43. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  44. * has been added to the <code>java.beans</code> package.
  45. * Please see {@link java.beans.XMLEncoder}.
  46. *
  47. * @beaninfo
  48. * attribute: isContainer true
  49. * description: A text component that can be marked up with attributes that are graphically represented.
  50. *
  51. * @author Timothy Prinzing
  52. * @version 1.90 12/19/03
  53. * @see javax.swing.text.StyledEditorKit
  54. */
  55. public class JTextPane extends JEditorPane {
  56. /**
  57. * Creates a new <code>JTextPane</code>. A new instance of
  58. * <code>StyledEditorKit</code> is
  59. * created and set, and the document model set to <code>null</code>.
  60. */
  61. public JTextPane() {
  62. super();
  63. setEditorKit(createDefaultEditorKit());
  64. }
  65. /**
  66. * Creates a new <code>JTextPane</code>, with a specified document model.
  67. * A new instance of <code>javax.swing.text.StyledEditorKit</code>
  68. * is created and set.
  69. *
  70. * @param doc the document model
  71. */
  72. public JTextPane(StyledDocument doc) {
  73. this();
  74. setStyledDocument(doc);
  75. }
  76. /**
  77. * Returns the class ID for the UI.
  78. *
  79. * @return the string "TextPaneUI"
  80. *
  81. * @see JComponent#getUIClassID
  82. * @see UIDefaults#getUI
  83. */
  84. public String getUIClassID() {
  85. return uiClassID;
  86. }
  87. /**
  88. * Associates the editor with a text document. This
  89. * must be a <code>StyledDocument</code>.
  90. *
  91. * @param doc the document to display/edit
  92. * @exception IllegalArgumentException if <code>doc</code> can't
  93. * be narrowed to a <code>StyledDocument</code> which is the
  94. * required type of model for this text component
  95. */
  96. public void setDocument(Document doc) {
  97. if (doc instanceof StyledDocument) {
  98. super.setDocument(doc);
  99. } else {
  100. throw new IllegalArgumentException("Model must be StyledDocument");
  101. }
  102. }
  103. /**
  104. * Associates the editor with a text document.
  105. * The currently registered factory is used to build a view for
  106. * the document, which gets displayed by the editor.
  107. *
  108. * @param doc the document to display/edit
  109. */
  110. public void setStyledDocument(StyledDocument doc) {
  111. super.setDocument(doc);
  112. }
  113. /**
  114. * Fetches the model associated with the editor.
  115. *
  116. * @return the model
  117. */
  118. public StyledDocument getStyledDocument() {
  119. return (StyledDocument) getDocument();
  120. }
  121. /**
  122. * Replaces the currently selected content with new content
  123. * represented by the given string. If there is no selection
  124. * this amounts to an insert of the given text. If there
  125. * is no replacement text this amounts to a removal of the
  126. * current selection. The replacement text will have the
  127. * attributes currently defined for input at the point of
  128. * insertion. If the document is not editable, beep and return.
  129. * <p>
  130. * This method is thread safe, although most Swing methods
  131. * are not. Please see
  132. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  133. * and Swing</A> for more information.
  134. *
  135. * @param content the content to replace the selection with
  136. */
  137. public void replaceSelection(String content) {
  138. replaceSelection(content, true);
  139. }
  140. private void replaceSelection(String content, boolean checkEditable) {
  141. if (checkEditable && !isEditable()) {
  142. UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this);
  143. return;
  144. }
  145. Document doc = getStyledDocument();
  146. if (doc != null) {
  147. try {
  148. Caret caret = getCaret();
  149. int p0 = Math.min(caret.getDot(), caret.getMark());
  150. int p1 = Math.max(caret.getDot(), caret.getMark());
  151. AttributeSet attr = getInputAttributes().copyAttributes();
  152. if (doc instanceof AbstractDocument) {
  153. ((AbstractDocument)doc).replace(p0, p1 - p0, content,attr);
  154. }
  155. else {
  156. if (p0 != p1) {
  157. doc.remove(p0, p1 - p0);
  158. }
  159. if (content != null && content.length() > 0) {
  160. doc.insertString(p0, content, attr);
  161. }
  162. }
  163. } catch (BadLocationException e) {
  164. UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this);
  165. }
  166. }
  167. }
  168. /**
  169. * Inserts a component into the document as a replacement
  170. * for the currently selected content. If there is no
  171. * selection the component is effectively inserted at the
  172. * current position of the caret. This is represented in
  173. * the associated document as an attribute of one character
  174. * of content.
  175. * <p>
  176. * The component given is the actual component used by the
  177. * JTextPane. Since components cannot be a child of more than
  178. * one container, this method should not be used in situations
  179. * where the model is shared by text components.
  180. * <p>
  181. * The component is placed relative to the text baseline
  182. * according to the value returned by
  183. * <code>Component.getAlignmentY</code>. For Swing components
  184. * this value can be conveniently set using the method
  185. * <code>JComponent.setAlignmentY</code>. For example, setting
  186. * a value of <code>0.75</code> will cause 75 percent of the
  187. * component to be above the baseline, and 25 percent of the
  188. * component to be below the baseline.
  189. * <p>
  190. * This method is thread safe, although most Swing methods
  191. * are not. Please see
  192. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  193. * and Swing</A> for more information.
  194. *
  195. * @param c the component to insert
  196. */
  197. public void insertComponent(Component c) {
  198. MutableAttributeSet inputAttributes = getInputAttributes();
  199. inputAttributes.removeAttributes(inputAttributes);
  200. StyleConstants.setComponent(inputAttributes, c);
  201. replaceSelection(" ", false);
  202. inputAttributes.removeAttributes(inputAttributes);
  203. }
  204. /**
  205. * Inserts an icon into the document as a replacement
  206. * for the currently selected content. If there is no
  207. * selection the icon is effectively inserted at the
  208. * current position of the caret. This is represented in
  209. * the associated document as an attribute of one character
  210. * of content.
  211. * <p>
  212. * This method is thread safe, although most Swing methods
  213. * are not. Please see
  214. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  215. * and Swing</A> for more information.
  216. *
  217. * @param g the icon to insert
  218. * @see Icon
  219. */
  220. public void insertIcon(Icon g) {
  221. MutableAttributeSet inputAttributes = getInputAttributes();
  222. inputAttributes.removeAttributes(inputAttributes);
  223. StyleConstants.setIcon(inputAttributes, g);
  224. replaceSelection(" ", false);
  225. inputAttributes.removeAttributes(inputAttributes);
  226. }
  227. /**
  228. * Adds a new style into the logical style hierarchy. Style attributes
  229. * resolve from bottom up so an attribute specified in a child
  230. * will override an attribute specified in the parent.
  231. *
  232. * @param nm the name of the style (must be unique within the
  233. * collection of named styles). The name may be <code>null</code>
  234. * if the style is unnamed, but the caller is responsible
  235. * for managing the reference returned as an unnamed style can't
  236. * be fetched by name. An unnamed style may be useful for things
  237. * like character attribute overrides such as found in a style
  238. * run.
  239. * @param parent the parent style. This may be <code>null</code>
  240. * if unspecified
  241. * attributes need not be resolved in some other style.
  242. * @return the new <code>Style</code>
  243. */
  244. public Style addStyle(String nm, Style parent) {
  245. StyledDocument doc = getStyledDocument();
  246. return doc.addStyle(nm, parent);
  247. }
  248. /**
  249. * Removes a named non-<code>null</code> style previously added to
  250. * the document.
  251. *
  252. * @param nm the name of the style to remove
  253. */
  254. public void removeStyle(String nm) {
  255. StyledDocument doc = getStyledDocument();
  256. doc.removeStyle(nm);
  257. }
  258. /**
  259. * Fetches a named non-<code>null</code> style previously added.
  260. *
  261. * @param nm the name of the style
  262. * @return the <code>Style</code>
  263. */
  264. public Style getStyle(String nm) {
  265. StyledDocument doc = getStyledDocument();
  266. return doc.getStyle(nm);
  267. }
  268. /**
  269. * Sets the logical style to use for the paragraph at the
  270. * current caret position. If attributes aren't explicitly set
  271. * for character and paragraph attributes they will resolve
  272. * through the logical style assigned to the paragraph, which
  273. * in term may resolve through some hierarchy completely
  274. * independent of the element hierarchy in the document.
  275. * <p>
  276. * This method is thread safe, although most Swing methods
  277. * are not. Please see
  278. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  279. * and Swing</A> for more information.
  280. *
  281. * @param s the logical style to assign to the paragraph,
  282. * or <code>null</code> for no style
  283. */
  284. public void setLogicalStyle(Style s) {
  285. StyledDocument doc = getStyledDocument();
  286. doc.setLogicalStyle(getCaretPosition(), s);
  287. }
  288. /**
  289. * Fetches the logical style assigned to the paragraph represented
  290. * by the current position of the caret, or <code>null</code>.
  291. *
  292. * @return the <code>Style</code>
  293. */
  294. public Style getLogicalStyle() {
  295. StyledDocument doc = getStyledDocument();
  296. return doc.getLogicalStyle(getCaretPosition());
  297. }
  298. /**
  299. * Fetches the character attributes in effect at the
  300. * current location of the caret, or <code>null</code>.
  301. *
  302. * @return the attributes, or <code>null</code>
  303. */
  304. public AttributeSet getCharacterAttributes() {
  305. StyledDocument doc = getStyledDocument();
  306. Element run = doc.getCharacterElement(getCaretPosition());
  307. if (run != null) {
  308. return run.getAttributes();
  309. }
  310. return null;
  311. }
  312. /**
  313. * Applies the given attributes to character
  314. * content. If there is a selection, the attributes
  315. * are applied to the selection range. If there
  316. * is no selection, the attributes are applied to
  317. * the input attribute set which defines the attributes
  318. * for any new text that gets inserted.
  319. * <p>
  320. * This method is thread safe, although most Swing methods
  321. * are not. Please see
  322. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  323. * and Swing</A> for more information.
  324. *
  325. * @param attr the attributes
  326. * @param replace if true, then replace the existing attributes first
  327. */
  328. public void setCharacterAttributes(AttributeSet attr, boolean replace) {
  329. int p0 = getSelectionStart();
  330. int p1 = getSelectionEnd();
  331. if (p0 != p1) {
  332. StyledDocument doc = getStyledDocument();
  333. doc.setCharacterAttributes(p0, p1 - p0, attr, replace);
  334. } else {
  335. MutableAttributeSet inputAttributes = getInputAttributes();
  336. if (replace) {
  337. inputAttributes.removeAttributes(inputAttributes);
  338. }
  339. inputAttributes.addAttributes(attr);
  340. }
  341. }
  342. /**
  343. * Fetches the current paragraph attributes in effect
  344. * at the location of the caret, or <code>null</code> if none.
  345. *
  346. * @return the attributes
  347. */
  348. public AttributeSet getParagraphAttributes() {
  349. StyledDocument doc = getStyledDocument();
  350. Element paragraph = doc.getParagraphElement(getCaretPosition());
  351. if (paragraph != null) {
  352. return paragraph.getAttributes();
  353. }
  354. return null;
  355. }
  356. /**
  357. * Applies the given attributes to paragraphs. If
  358. * there is a selection, the attributes are applied
  359. * to the paragraphs that intersect the selection.
  360. * If there is no selection, the attributes are applied
  361. * to the paragraph at the current caret position.
  362. * <p>
  363. * This method is thread safe, although most Swing methods
  364. * are not. Please see
  365. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  366. * and Swing</A> for more information.
  367. *
  368. * @param attr the non-<code>null</code> attributes
  369. * @param replace if true, replace the existing attributes first
  370. */
  371. public void setParagraphAttributes(AttributeSet attr, boolean replace) {
  372. int p0 = getSelectionStart();
  373. int p1 = getSelectionEnd();
  374. StyledDocument doc = getStyledDocument();
  375. doc.setParagraphAttributes(p0, p1 - p0, attr, replace);
  376. }
  377. /**
  378. * Gets the input attributes for the pane.
  379. *
  380. * @return the attributes
  381. */
  382. public MutableAttributeSet getInputAttributes() {
  383. return getStyledEditorKit().getInputAttributes();
  384. }
  385. /**
  386. * Gets the editor kit.
  387. *
  388. * @return the editor kit
  389. */
  390. protected final StyledEditorKit getStyledEditorKit() {
  391. return (StyledEditorKit) getEditorKit();
  392. }
  393. /**
  394. * @see #getUIClassID
  395. * @see #readObject
  396. */
  397. private static final String uiClassID = "TextPaneUI";
  398. /**
  399. * See <code>readObject</code> and <code>writeObject</code> in
  400. * <code>JComponent</code> for more
  401. * information about serialization in Swing.
  402. *
  403. * @param s the output stream
  404. */
  405. private void writeObject(ObjectOutputStream s) throws IOException {
  406. s.defaultWriteObject();
  407. if (getUIClassID().equals(uiClassID)) {
  408. byte count = JComponent.getWriteObjCounter(this);
  409. JComponent.setWriteObjCounter(this, --count);
  410. if (count == 0 && ui != null) {
  411. ui.installUI(this);
  412. }
  413. }
  414. }
  415. // --- JEditorPane ------------------------------------
  416. /**
  417. * Creates the <code>EditorKit</code> to use by default. This
  418. * is implemented to return <code>javax.swing.text.StyledEditorKit</code>.
  419. *
  420. * @return the editor kit
  421. */
  422. protected EditorKit createDefaultEditorKit() {
  423. return new StyledEditorKit();
  424. }
  425. /**
  426. * Sets the currently installed kit for handling
  427. * content. This is the bound property that
  428. * establishes the content type of the editor.
  429. *
  430. * @param kit the desired editor behavior
  431. * @exception IllegalArgumentException if kit is not a
  432. * <code>StyledEditorKit</code>
  433. */
  434. public final void setEditorKit(EditorKit kit) {
  435. if (kit instanceof StyledEditorKit) {
  436. super.setEditorKit(kit);
  437. } else {
  438. throw new IllegalArgumentException("Must be StyledEditorKit");
  439. }
  440. }
  441. /**
  442. * Returns a string representation of this <code>JTextPane</code>.
  443. * This method
  444. * is intended to be used only for debugging purposes, and the
  445. * content and format of the returned string may vary between
  446. * implementations. The returned string may be empty but may not
  447. * be <code>null</code>.
  448. *
  449. * @return a string representation of this <code>JTextPane</code>
  450. */
  451. protected String paramString() {
  452. return super.paramString();
  453. }
  454. }