1. /*
  2. * @(#)JEditorPane.java 1.97 00/04/06
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing;
  11. import java.awt.*;
  12. import java.awt.event.*;
  13. import java.net.*;
  14. import java.util.*;
  15. import java.io.*;
  16. import java.util.*;
  17. import javax.swing.plaf.*;
  18. import javax.swing.text.*;
  19. import javax.swing.event.*;
  20. import javax.swing.text.html.*;
  21. import javax.accessibility.*;
  22. /**
  23. * A text component to edit various kinds of content.
  24. * You can find how-to information and examples of using editor panes in
  25. * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/text.html">Using Text Components</a>,
  26. * a section in <em>The Java Tutorial.</em>
  27. *
  28. * <p>
  29. * This component uses implementations of the
  30. * <code>EditorKit</code> to accomplish its behavior. It effectively
  31. * morphs into the proper kind of text editor for the kind
  32. * of content it is given. The content type that editor is bound
  33. * to at any given time is determined by the <code>EditorKit</code> currently
  34. * installed. If the content is set to a new URL, its type is used
  35. * to determine the <code>EditorKit</code> that should be used to
  36. * load the content.
  37. * <p>
  38. * By default, the following types of content are known:
  39. * <dl>
  40. * <dt><b>text/plain</b>
  41. * <dd>Plain text, which is the default the type given isn't
  42. * recognized. The kit used in this case is an extension of
  43. * <code>DefaultEditorKit</code> that produces a wrapped plain text view.
  44. * <dt><b>text/html</b>
  45. * <dd>HTML text. The kit used in this case is the class
  46. * <code>javax.swing.text.html.HTMLEditorKit</code>
  47. * which provides HTML 3.2 support.
  48. * <dt><b>text/rtf</b>
  49. * <dd>RTF text. The kit used in this case is the class
  50. * <code>javax.swing.text.rtf.RTFEditorKit</code>
  51. * which provides a limited support of the Rich Text Format.
  52. * </dl>
  53. * <p>
  54. * There are several ways to load content into this component.
  55. * <ol>
  56. * <li>
  57. * The <a href="#setText">setText</a> method can be used to initialize
  58. * the component from a string. In this case the current
  59. * <code>EditorKit</code> will be used, and the content type will be
  60. * expected to be of this type.
  61. * <li>
  62. * The <a href="#read">read</a> method can be used to initialize the
  63. * component from a Reader. Note that if the content type is HTML,
  64. * relative references (e.g. for things like images) can't be resolved
  65. * unless the <base> tag is used or the <em>Base</em> property
  66. * on HTMLDocument is set. In this case the current <code>EditorKit</code>
  67. * will be used, and the content type will be expected to be of this
  68. * type.
  69. * <li>
  70. * The <a href="#setPage">setPage</a> method can be used to initialize
  71. * the component from a URL. In this case, the content type will be
  72. * determined from the URL, and the registered <code>EditorKit</code>
  73. * for that content type will be set.
  74. * </ol>
  75. * <p>
  76. * For the keyboard keys used by this component in the standard Look and
  77. * Feel (L&F) renditions, see the
  78. * <a href="doc-files/Key-Index.html#JEditorPane">JEditorPane</a> key assignments.
  79. * <p>
  80. * Some kinds of content may provide hyperlink support by generating
  81. * hyperlink events. The HTML <code>EditorKit</code> will generate
  82. * hyperlink events if the <code>JEditorPane</code> is <em>not editable</em>
  83. * (<code>JEditorPane.setEditable(false);</code> has been called).
  84. * If HTML frames are embedded in the document, the typical response would be
  85. * to change a portion of the current document. The following code
  86. * fragment is a possible hyperlink listener implementation, that treats
  87. * HTML frame events specially, and simply displays any other activated
  88. * hyperlinks.
  89. * <code><pre>
  90.   class Hyperactive implements HyperlinkListener {
  91.  
  92.   public void hyperlinkUpdate(HyperlinkEvent e) {
  93.   if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
  94.   JEditorPane pane = (JEditorPane) e.getSource();
  95.   if (e instanceof HTMLFrameHyperlinkEvent) {
  96.   HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e;
  97.   HTMLDocument doc = (HTMLDocument)pane.getDocument();
  98.   doc.processHTMLFrameHyperlinkEvent(evt);
  99.   } else {
  100.   try {
  101.   pane.setPage(e.getURL());
  102.   } catch (Throwable t) {
  103.   t.printStackTrace();
  104.   }
  105.   }
  106.   }
  107.   }
  108.   }
  109. * </pre></code>
  110. * <p>
  111. * Culturally dependent information in some documents is handled through
  112. * a mechanism called character encoding. Character encoding is an
  113. * unambiguous mapping of the members of a character set (letters, ideographs,
  114. * digits, symbols, or control functions) to specific numeric code values. It
  115. * represents the way the file is stored. Example character encodings are
  116. * ISO-8859-1, ISO-8859-5, Shift-jis, Euc-jp, and UTF-8. When the file is
  117. * passed to an user agent (<code>JEditorPane</code>) it is converted to
  118. * the document character set (ISO-10646 aka Unicode).
  119. * <p>
  120. * There are multiple ways to get a character set mapping to happen
  121. * with <code>JEditorPane</code>.
  122. * <ol>
  123. * <li>
  124. * One way is to specify the character set as a parameter of the MIME
  125. * type. This will be established by a call to the
  126. * <a href="#setContentType">setContentType</a> method. If the content
  127. * is loaded by the <a href="#setPage">setPage</a> method the content
  128. * type will have been set according to the specification of the URL.
  129. * It the file is loaded directly, the content type would be expected to
  130. * have been set prior to loading.
  131. * <li>
  132. * Another way the character set can be specified is in the document itself.
  133. * This requires reading the document prior to determining the character set
  134. * that is desired. To handle this, it is expected that the
  135. * <code>EditorKit</code>.read operation throw a
  136. * <code>ChangedCharSetException</code> which will
  137. * be caught. The read is then restarted with a new Reader that uses
  138. * the character set specified in the <code>ChangedCharSetException</code>
  139. * (which is an <code>IOException</code>).
  140. * </ol>
  141. * <p>
  142. * <strong>Warning:</strong>
  143. * Serialized objects of this class will not be compatible with
  144. * future Swing releases. The current serialization support is appropriate
  145. * for short term storage or RMI between applications running the same
  146. * version of Swing. A future release of Swing will provide support for
  147. * long term persistence.
  148. *
  149. * @beaninfo
  150. * attribute: isContainer false
  151. * description: A text component to edit various types of content.
  152. *
  153. * @author Timothy Prinzing
  154. * @version 1.97 04/06/00
  155. */
  156. public class JEditorPane extends JTextComponent {
  157. /**
  158. * Creates a new <code>JEditorPane</code>.
  159. * The document model is set to <code>null</code>.
  160. */
  161. public JEditorPane() {
  162. super();
  163. }
  164. /**
  165. * Creates a <code>JEditorPane</code> based on a specified URL for input.
  166. *
  167. * @param initialPage the URL
  168. * @exception IOException if the URL is <code>null</code>
  169. * or cannot be accessed
  170. */
  171. public JEditorPane(URL initialPage) throws IOException {
  172. this();
  173. setPage(initialPage);
  174. }
  175. /**
  176. * Creates a <code>JEditorPane</code> based on a string containing
  177. * a URL specification.
  178. *
  179. * @param url the URL
  180. * @exception IOException if the URL is <code>null</code> or
  181. * cannot be accessed
  182. */
  183. public JEditorPane(String url) throws IOException {
  184. this();
  185. setPage(url);
  186. }
  187. /**
  188. * Creates a <code>JEditorPane</code> that has been initialized
  189. * to the given text. This is a convenience constructor that calls the
  190. * <code>setContentType</code> and <code>setText</code> methods.
  191. *
  192. * @param type mime type of the given text
  193. * @param text the text to initialize with
  194. */
  195. public JEditorPane(String type, String text) {
  196. this();
  197. setContentType(type);
  198. setText(text);
  199. }
  200. /**
  201. * Adds a hyperlink listener for notification of any changes, for example
  202. * when a link is selected and entered.
  203. *
  204. * @param listener the listener
  205. */
  206. public synchronized void addHyperlinkListener(HyperlinkListener listener) {
  207. listenerList.add(HyperlinkListener.class, listener);
  208. }
  209. /**
  210. * Removes a hyperlink listener.
  211. *
  212. * @param listener the listener
  213. */
  214. public synchronized void removeHyperlinkListener(HyperlinkListener listener) {
  215. listenerList.remove(HyperlinkListener.class, listener);
  216. }
  217. /**
  218. * Notifies all listeners that have registered interest for
  219. * notification on this event type. This is normally called
  220. * by the currently installed <code>EditorKit</code> if a content type
  221. * that supports hyperlinks is currently active and there
  222. * was activity with a link. The listener list is processed
  223. * last to first.
  224. *
  225. * @param e the event
  226. * @see EventListenerList
  227. */
  228. public void fireHyperlinkUpdate(HyperlinkEvent e) {
  229. // Guaranteed to return a non-null array
  230. Object[] listeners = listenerList.getListenerList();
  231. // Process the listeners last to first, notifying
  232. // those that are interested in this event
  233. for (int i = listeners.length-2; i>=0; i-=2) {
  234. if (listeners[i]==HyperlinkListener.class) {
  235. ((HyperlinkListener)listeners[i+1]).hyperlinkUpdate(e);
  236. }
  237. }
  238. }
  239. /**
  240. * Sets the current URL being displayed. The content type of the
  241. * pane is set, and if the editor kit for the pane is
  242. * non-<code>null</code>, then
  243. * a new default document is created and the URL is read into it.
  244. * If the URL contains and reference location, the location will
  245. * be scrolled to by calling the <code>scrollToReference</code>
  246. * method. If the desired URL is not the one currently being
  247. * displayed, the <code>getStream</code> method is called to
  248. * give subclasses control over the stream provided.
  249. * <p>
  250. * This may load either synchronously or asynchronously
  251. * depending upon the document returned by the <code>EditorKit</code>.
  252. * If the <code>Document</code> is of type
  253. * <code>AbstractDocument</code> and has a value returned by
  254. * <code>AbstractDocument.getAsynchronousLoadPriority</code>
  255. * that is greater than or equal to zero, the page will be
  256. * loaded on a separate thread using that priority.
  257. * <p>
  258. * If the document is loaded synchronously, it will be
  259. * filled in with the stream prior to being installed into
  260. * the editor with a call to <code>setDocument</code>, which
  261. * is bound and will fire a property change event. If an
  262. * <code>IOException</code> is thrown the partially loaded
  263. * document will
  264. * be discarded and neither the document or page property
  265. * change events will be fired. If the document is
  266. * successfully loaded and installed, a view will be
  267. * built for it by the UI which will then be scrolled if
  268. * necessary, and then the page property change event
  269. * will be fired.
  270. * <p>
  271. * If the document is loaded asynchronously, the document
  272. * will be installed into the editor immediately using a
  273. * call to <code>setDocument</code> which will fire a
  274. * document property change event, then a thread will be
  275. * created which will begin doing the actual loading.
  276. * In this case, the page property change event will not be
  277. * fired by the call to this method directly, but rather will be
  278. * fired when the thread doing the loading has finished.
  279. * Since the calling thread can not throw an <code>IOException</code>
  280. * in the event of failure on the other thread, the page
  281. * property change event will be fired when the other
  282. * thread is done whether the load was successful or not.
  283. *
  284. * @param page the URL of the page
  285. * @exception IOException for a <code>null</code> or invalid
  286. * page specification, or exception from the stream being read
  287. * @see #getPage
  288. * @beaninfo
  289. * description: the URL used to set content
  290. * bound: true
  291. * expert: true
  292. */
  293. public void setPage(URL page) throws IOException {
  294. if (page == null) {
  295. throw new IOException("invalid url");
  296. }
  297. URL loaded = getPage();
  298. // reset scrollbar
  299. scrollRectToVisible(new Rectangle(0,0,1,1));
  300. boolean reloaded = false;
  301. if ((loaded == null) || (! loaded.sameFile(page))) {
  302. // different url, load the new content
  303. InputStream in = getStream(page);
  304. if (kit != null) {
  305. Document doc = kit.createDefaultDocument();
  306. if (pageProperties != null) {
  307. // transfer properties discovered in stream to the
  308. // document property collection.
  309. for (Enumeration e = pageProperties.keys(); e.hasMoreElements() ;) {
  310. Object key = e.nextElement();
  311. doc.putProperty(key, pageProperties.get(key));
  312. }
  313. pageProperties.clear();
  314. }
  315. if (doc.getProperty(Document.StreamDescriptionProperty) == null) {
  316. doc.putProperty(Document.StreamDescriptionProperty, page);
  317. }
  318. // At this point, one could either load up the model with no
  319. // view notifications slowing it down (i.e. best synchronous
  320. // behavior) or set the model and start to feed it on a seperate
  321. // thread (best asynchronous behavior).
  322. if (doc instanceof AbstractDocument) {
  323. AbstractDocument adoc = (AbstractDocument) doc;
  324. int p = adoc.getAsynchronousLoadPriority();
  325. if (p >= 0) {
  326. // load asynchronously
  327. setDocument(doc);
  328. Thread pl = new PageLoader(doc, in, p, loaded, page);
  329. pl.start();
  330. return;
  331. }
  332. }
  333. read(in, doc);
  334. setDocument(doc);
  335. reloaded = true;
  336. }
  337. }
  338. final String reference = page.getRef();
  339. if (reference != null) {
  340. if (!reloaded) {
  341. scrollToReference(reference);
  342. }
  343. else {
  344. // Have to scroll after painted.
  345. SwingUtilities.invokeLater(new Runnable() {
  346. public void run() {
  347. scrollToReference(reference);
  348. }
  349. });
  350. }
  351. }
  352. firePropertyChange("page", loaded, page);
  353. }
  354. /**
  355. * This method initializes from a stream. If the kit is
  356. * set to be of type <code>HTMLEditorKit</code>, and the
  357. * <code>desc</code> parameter is an <code>HTMLDocument</code>,
  358. * then it invokes the <code>HTMLEditorKit</code> to initiate
  359. * the read. Otherwise it calls the superclass
  360. * method which loads the model as plain text.
  361. *
  362. * @param in the stream from which to read
  363. * @param desc an object describing the stream
  364. * @exception IOException as thrown by the stream being
  365. * used to initialize
  366. * @see JTextComponent#read
  367. * @see #setDocument
  368. */
  369. public void read(InputStream in, Object desc) throws IOException {
  370. if (desc instanceof HTMLDocument &&
  371. kit instanceof HTMLEditorKit) {
  372. HTMLDocument hdoc = (HTMLDocument) desc;
  373. setDocument(hdoc);
  374. read(in, hdoc);
  375. } else {
  376. String charset = (String) getClientProperty("charset");
  377. Reader r = (charset != null) ? new InputStreamReader(in, charset) :
  378. new InputStreamReader(in);
  379. super.read(r, desc);
  380. }
  381. }
  382. /**
  383. * This method invokes the <code>EditorKit</code> to initiate a
  384. * read. In the case where a <code>ChangedCharSetException</code>
  385. * is thrown this exception will contain the new CharSet.
  386. * Therefore the <code>read</code> operation
  387. * is then restarted after building a new Reader with the new charset.
  388. *
  389. * @param in the inputstream to use
  390. * @param doc the document to load
  391. *
  392. */
  393. void read(InputStream in, Document doc) throws IOException {
  394. try {
  395. String charset = (String) getClientProperty("charset");
  396. Reader r = (charset != null) ? new InputStreamReader(in, charset) :
  397. new InputStreamReader(in);
  398. kit.read(r, doc, 0);
  399. } catch (BadLocationException e) {
  400. throw new IOException(e.getMessage());
  401. } catch (ChangedCharSetException e1) {
  402. String charSetSpec = e1.getCharSetSpec();
  403. if (e1.keyEqualsCharSet()) {
  404. putClientProperty("charset", charSetSpec);
  405. } else {
  406. setCharsetFromContentTypeParameters(charSetSpec);
  407. }
  408. in.close();
  409. URL url = (URL)doc.getProperty(Document.StreamDescriptionProperty);
  410. URLConnection conn = url.openConnection();
  411. in = conn.getInputStream();
  412. try {
  413. doc.remove(0, doc.getLength());
  414. } catch (BadLocationException e) {}
  415. doc.putProperty("IgnoreCharsetDirective", new Boolean(true));
  416. read(in, doc);
  417. }
  418. }
  419. /**
  420. * Thread to load a stream into the text document model.
  421. */
  422. class PageLoader extends Thread {
  423. /**
  424. * Construct an asynchronous page loader.
  425. */
  426. PageLoader(Document doc, InputStream in, int priority, URL old,
  427. URL page) {
  428. setPriority(priority);
  429. this.in = in;
  430. this.old = old;
  431. this.page = page;
  432. this.doc = doc;
  433. }
  434. /**
  435. * Try to load the document, then scroll the view
  436. * to the reference (if specified). When done, fire
  437. * a page property change event.
  438. */
  439. public void run() {
  440. try {
  441. read(in, doc);
  442. URL page = (URL) doc.getProperty(Document.StreamDescriptionProperty);
  443. String reference = page.getRef();
  444. if (reference != null) {
  445. // scroll the page if necessary, but do it on the
  446. // event thread... that is the only guarantee that
  447. // modelToView can be safely called.
  448. Runnable callScrollToReference = new Runnable() {
  449. public void run() {
  450. URL u = (URL) getDocument().getProperty
  451. (Document.StreamDescriptionProperty);
  452. String ref = u.getRef();
  453. scrollToReference(ref);
  454. }
  455. };
  456. SwingUtilities.invokeLater(callScrollToReference);
  457. }
  458. } catch (IOException ioe) {
  459. getToolkit().beep();
  460. } finally {
  461. firePropertyChange("page", old, page);
  462. }
  463. }
  464. /**
  465. * The stream to load the document with
  466. */
  467. InputStream in;
  468. /**
  469. * URL of the old page that was replaced (for the property change event)
  470. */
  471. URL old;
  472. /**
  473. * URL of the page being loaded (for the property change event)
  474. */
  475. URL page;
  476. /**
  477. * The Document instance to load into. This is cached in case a
  478. * new Document is created between the time the thread this is created
  479. * and run.
  480. */
  481. Document doc;
  482. }
  483. /**
  484. * Fetches a stream for the given URL, which is about to
  485. * be loaded by the <code>setPage</code> method. By
  486. * default, this simply opens the URL and returns the
  487. * stream. This can be reimplemented to do useful things
  488. * like fetch the stream from a cache, monitor the progress
  489. * of the stream, etc.
  490. * <p>
  491. * This method is expected to have the the side effect of
  492. * establishing the content type, and therefore setting the
  493. * appropriate <code>EditorKit</code> to use for loading the stream.
  494. * <p>
  495. * If this the stream was an http connection, redirects
  496. * will be followed and the resulting URL will be set as
  497. * the <code>Document.StreamDescriptionProperty</code> so that relative
  498. * URL's can be properly resolved.
  499. *
  500. * @param page the URL of the page
  501. */
  502. protected InputStream getStream(URL page) throws IOException {
  503. URLConnection conn = page.openConnection();
  504. if (conn instanceof HttpURLConnection) {
  505. HttpURLConnection hconn = (HttpURLConnection) conn;
  506. hconn.setInstanceFollowRedirects(false);
  507. int response = hconn.getResponseCode();
  508. boolean redirect = (response >= 300 && response <= 399);
  509. /*
  510. * In the case of a redirect, we want to actually change the URL
  511. * that was input to the new, redirected URL
  512. */
  513. if (redirect) {
  514. String loc = conn.getHeaderField("Location");
  515. if (loc.startsWith("http", 0)) {
  516. page = new URL(loc);
  517. } else {
  518. page = new URL(page, loc);
  519. }
  520. return getStream(page);
  521. }
  522. }
  523. if (pageProperties == null) {
  524. pageProperties = new Hashtable();
  525. }
  526. String type = conn.getContentType();
  527. if (type != null) {
  528. setContentType(type);
  529. pageProperties.put("content-type", type);
  530. }
  531. pageProperties.put(Document.StreamDescriptionProperty, page);
  532. String enc = conn.getContentEncoding();
  533. if (enc != null) {
  534. pageProperties.put("content-encoding", enc);
  535. }
  536. InputStream in = conn.getInputStream();
  537. return in;
  538. }
  539. /**
  540. * Scrolls the view to the given reference location
  541. * (that is, the value returned by the <code>UL.getRef</code>
  542. * method for the URL being displayed). By default, this
  543. * method only knows how to locate a reference in an
  544. * HTMLDocument. The implementation calls the
  545. * <code>scrollRectToVisible</code> method to
  546. * accomplish the actual scrolling. If scrolling to a
  547. * reference location is needed for document types other
  548. * than HTML, this method should be reimplemented.
  549. * This method will have no effect if the component
  550. * is not visible.
  551. *
  552. * @param reference the named location to scroll to
  553. */
  554. protected void scrollToReference(String reference) {
  555. Document d = getDocument();
  556. if (d instanceof HTMLDocument) {
  557. HTMLDocument doc = (HTMLDocument) d;
  558. HTMLDocument.Iterator iter = doc.getIterator(HTML.Tag.A);
  559. for (; iter.isValid(); iter.next()) {
  560. AttributeSet a = iter.getAttributes();
  561. String nm = (String) a.getAttribute(HTML.Attribute.NAME);
  562. if ((nm != null) && nm.equals(reference)) {
  563. // found a matching reference in the document.
  564. try {
  565. Rectangle r = modelToView(iter.getStartOffset());
  566. if (r != null) {
  567. // the view is visible, scroll it to the
  568. // center of the current visible area.
  569. Rectangle vis = getVisibleRect();
  570. //r.y -= (vis.height / 2);
  571. r.height = vis.height;
  572. scrollRectToVisible(r);
  573. }
  574. } catch (BadLocationException ble) {
  575. getToolkit().beep();
  576. }
  577. }
  578. }
  579. }
  580. }
  581. /**
  582. * Gets the current URL being displayed. If a URL was
  583. * not specified in the creation of the document, this
  584. * will return <code>null</code>, and relative URL's will not be
  585. * resolved.
  586. *
  587. * @return the URL, or <code>null</code> if none
  588. */
  589. public URL getPage() {
  590. return (URL) getDocument().getProperty(Document.StreamDescriptionProperty);
  591. }
  592. /**
  593. * Sets the current URL being displayed.
  594. *
  595. * @param url the URL for display
  596. * @exception IOException for a <code>null</code> or invalid URL
  597. * specification
  598. */
  599. public void setPage(String url) throws IOException {
  600. if (url == null) {
  601. throw new IOException("invalid url");
  602. }
  603. URL page = new URL(url);
  604. setPage(page);
  605. }
  606. /**
  607. * Gets the class ID for the UI.
  608. *
  609. * @return the string "EditorPaneUI"
  610. * @see JComponent#getUIClassID
  611. * @see UIDefaults#getUI
  612. */
  613. public String getUIClassID() {
  614. return uiClassID;
  615. }
  616. /**
  617. * Creates the default editor kit (<code>PlainEditorKit</code>) for when
  618. * the component is first created.
  619. *
  620. * @return the editor kit
  621. */
  622. protected EditorKit createDefaultEditorKit() {
  623. return new PlainEditorKit();
  624. }
  625. /**
  626. * Fetches the currently installed kit for handling content.
  627. * <code>createDefaultEditorKit</code> is called to set up a default
  628. * if necessary.
  629. *
  630. * @return the editor kit
  631. */
  632. public EditorKit getEditorKit() {
  633. if (kit == null) {
  634. kit = createDefaultEditorKit();
  635. }
  636. return kit;
  637. }
  638. /**
  639. * Gets the type of content that this editor
  640. * is currently set to deal with. This is
  641. * defined to be the type associated with the
  642. * currently installed <code>EditorKit</code>.
  643. *
  644. * @return the content type, <code>null</code> if no editor kit set
  645. */
  646. public final String getContentType() {
  647. return (kit != null) ? kit.getContentType() : null;
  648. }
  649. /**
  650. * Sets the type of content that this editor
  651. * handles. This calls <code>getEditorKitForContentType</code>,
  652. * and then <code>setEditorKit</code> if an editor kit can
  653. * be successfully located. This is mostly convenience method
  654. * that can be used as an alternative to calling
  655. * <code>setEditorKit</code> directly.
  656. * <p>
  657. * If there is a charset definition specified as a parameter
  658. * of the content type specification, it will be used when
  659. * loading input streams using the associated <code>EditorKit</code>.
  660. * For example if the type is specified as
  661. * <code>text/html; charset=EUC-JP</code> the content
  662. * will be loaded using the <code>EditorKit</code> registered for
  663. * <code>text/html</code> and the Reader provided to
  664. * the <code>EditorKit</code> to load unicode into the document will
  665. * use the <code>EUC-JP</code> charset for translating
  666. * to unicode.
  667. *
  668. * @param type the non-<code>null</code> mime type for the content editing
  669. * support
  670. * @see #getContentType
  671. * @beaninfo
  672. * description: the type of content
  673. */
  674. public final void setContentType(String type) {
  675. // The type could have optional info is part of it,
  676. // for example some charset info. We need to strip that
  677. // of and save it.
  678. int parm = type.indexOf(";");
  679. if (parm > -1) {
  680. // Save the paramList.
  681. String paramList = type.substring(parm);
  682. // update the content type string.
  683. type = type.substring(0, parm).trim();
  684. if (type.toLowerCase().startsWith("text/")) {
  685. setCharsetFromContentTypeParameters(paramList);
  686. }
  687. }
  688. if ((kit == null) || (! type.equals(kit.getContentType()))) {
  689. EditorKit k = getEditorKitForContentType(type);
  690. if (k != null) {
  691. setEditorKit(k);
  692. }
  693. }
  694. }
  695. /**
  696. * This method gets the charset information specified as part
  697. * of the content type in the http header information.
  698. */
  699. private void setCharsetFromContentTypeParameters(String paramlist) {
  700. String charset = null;
  701. try {
  702. // paramlist is handed to us with a leading ';', strip it.
  703. int semi = paramlist.indexOf(';');
  704. if (semi > -1 && semi < paramlist.length()-1) {
  705. paramlist = paramlist.substring(semi + 1);
  706. }
  707. if (paramlist.length() > 0) {
  708. // parse the paramlist into attr-value pairs & get the
  709. // charset pair's value
  710. HeaderParser hdrParser = new HeaderParser(paramlist);
  711. charset = hdrParser.findValue("charset");
  712. if (charset != null) {
  713. putClientProperty("charset", charset);
  714. }
  715. }
  716. }
  717. catch (IndexOutOfBoundsException e) {
  718. // malformed parameter list, use charset we have
  719. }
  720. catch (NullPointerException e) {
  721. // malformed parameter list, use charset we have
  722. }
  723. catch (Exception e) {
  724. // malformed parameter list, use charset we have; but complain
  725. System.err.println("JEditorPane.getCharsetFromContentTypeParameters failed on: " + paramlist);
  726. e.printStackTrace();
  727. }
  728. }
  729. /**
  730. * Sets the currently installed kit for handling
  731. * content. This is the bound property that
  732. * establishes the content type of the editor.
  733. * Any old kit is first deinstalled, then if kit is
  734. * non-<code>null</code>,
  735. * the new kit is installed, and a default document created for it.
  736. * A <code>PropertyChange</code> event ("editorKit") is always fired when
  737. * <code>setEditorKit</code> is called.
  738. * <p>
  739. * <em>NOTE: This has the side effect of changing the model,
  740. * because the <code>EditorKit</code> is the source of how a
  741. * particular type
  742. * of content is modeled. This method will cause <code>setDocument</code>
  743. * to be called on behalf of the caller to ensure integrity
  744. * of the internal state.</em>
  745. *
  746. * @param kit the desired editor behavior
  747. * @see #getEditorKit
  748. * @beaninfo
  749. * description: the currently installed kit for handling content
  750. * bound: true
  751. * expert: true
  752. */
  753. public void setEditorKit(EditorKit kit) {
  754. EditorKit old = this.kit;
  755. if (old != null) {
  756. old.deinstall(this);
  757. }
  758. this.kit = kit;
  759. if (this.kit != null) {
  760. this.kit.install(this);
  761. setDocument(this.kit.createDefaultDocument());
  762. }
  763. firePropertyChange("editorKit", old, kit);
  764. }
  765. /**
  766. * Fetches the editor kit to use for the given type
  767. * of content. This is called when a type is requested
  768. * that doesn't match the currently installed type.
  769. * If the component doesn't have an <code>EditorKit</code> registered
  770. * for the given type, it will try to create an
  771. * <code>EditorKit</code> from the default <code>EditorKit</code> registry.
  772. * If that fails, a <code>PlainEditorKit</code> is used on the
  773. * assumption that all text documents can be represented
  774. * as plain text.
  775. * <p>
  776. * This method can be reimplemented to use some
  777. * other kind of type registry. This can
  778. * be reimplemented to use the Java Activation
  779. * Framework, for example.
  780. *
  781. * @param type the non-</code>null</code> content type
  782. * @return the editor kit
  783. */
  784. public EditorKit getEditorKitForContentType(String type) {
  785. if (typeHandlers == null) {
  786. typeHandlers = new Hashtable(3);
  787. }
  788. EditorKit k = (EditorKit) typeHandlers.get(type);
  789. if (k == null) {
  790. k = createEditorKitForContentType(type);
  791. if (k != null) {
  792. setEditorKitForContentType(type, k);
  793. }
  794. }
  795. if (k == null) {
  796. k = createDefaultEditorKit();
  797. }
  798. return k;
  799. }
  800. /**
  801. * Directly sets the editor kit to use for the given type. A
  802. * look-and-feel implementation might use this in conjunction
  803. * with <code>createEditorKitForContentType</code> to install handlers for
  804. * content types with a look-and-feel bias.
  805. *
  806. * @param type the non-<code>null</code> content type
  807. * @param k the editor kit to be set
  808. */
  809. public void setEditorKitForContentType(String type, EditorKit k) {
  810. if (typeHandlers == null) {
  811. typeHandlers = new Hashtable(3);
  812. }
  813. typeHandlers.put(type, k);
  814. }
  815. /**
  816. * Replaces the currently selected content with new content
  817. * represented by the given string. If there is no selection
  818. * this amounts to an insert of the given text. If there
  819. * is no replacement text (i.e. the content string is empty
  820. * or <code>null</code>) this amounts to a removal of the
  821. * current selection. The replacement text will have the
  822. * attributes currently defined for input. If the component is not
  823. * editable, beep and return.
  824. * <p>
  825. * This method is thread safe, although most Swing methods
  826. * are not. Please see
  827. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  828. * and Swing</A> for more information.
  829. *
  830. * @param content the content to replace the selection with. This
  831. * value can be <code>null</code>
  832. */
  833. public void replaceSelection(String content) {
  834. if (! isEditable()) {
  835. getToolkit().beep();
  836. return;
  837. }
  838. EditorKit kit = getEditorKit();
  839. if(kit instanceof StyledEditorKit) {
  840. try {
  841. Document doc = getDocument();
  842. Caret caret = getCaret();
  843. int p0 = Math.min(caret.getDot(), caret.getMark());
  844. int p1 = Math.max(caret.getDot(), caret.getMark());
  845. if (p0 != p1) {
  846. doc.remove(p0, p1 - p0);
  847. }
  848. if (content != null && content.length() > 0) {
  849. doc.insertString(p0, content, ((StyledEditorKit)kit).
  850. getInputAttributes());
  851. }
  852. } catch (BadLocationException e) {
  853. getToolkit().beep();
  854. }
  855. }
  856. else {
  857. super.replaceSelection(content);
  858. }
  859. }
  860. /**
  861. * Creates a handler for the given type from the default registry
  862. * of editor kits. The registry is created if necessary. If the
  863. * registered class has not yet been loaded, an attempt
  864. * is made to dynamically load the prototype of the kit for the
  865. * given type. If the type was registered with a <code>ClassLoader</code>,
  866. * that <code>ClassLoader</code> will be used to load the prototype.
  867. * If there was no registered <code>ClassLoader</code>,
  868. * <code>Class.forName</code> will be used to load the prototype.
  869. * <p>
  870. * Once a prototype <code>EditorKit</code> instance is successfully
  871. * located, it is cloned and the clone is returned.
  872. *
  873. * @param type the content type
  874. * @return the editor kit, or <code>null</code> if there is nothing
  875. * registered for the given type
  876. */
  877. public static EditorKit createEditorKitForContentType(String type) {
  878. EditorKit k = null;
  879. Hashtable kitRegistry = getKitRegisty();
  880. k = (EditorKit) kitRegistry.get(type);
  881. if (k == null) {
  882. // try to dynamically load the support
  883. String classname = (String) getKitTypeRegistry().get(type);
  884. ClassLoader loader = (ClassLoader) getKitLoaderRegistry().get(type);
  885. try {
  886. Class c;
  887. if (loader != null) {
  888. c = loader.loadClass(classname);
  889. } else {
  890. c = Class.forName(classname);
  891. }
  892. k = (EditorKit) c.newInstance();
  893. kitRegistry.put(type, k);
  894. } catch (Throwable e) {
  895. k = null;
  896. }
  897. }
  898. // create a copy of the prototype or null if there
  899. // is no prototype.
  900. if (k != null) {
  901. return (EditorKit) k.clone();
  902. }
  903. return null;
  904. }
  905. /**
  906. * Establishes the default bindings of <code>type</code> to
  907. * <code>classname</code>.
  908. * The class will be dynamically loaded later when actually
  909. * needed, and can be safely changed before attempted uses
  910. * to avoid loading unwanted classes. The prototype
  911. * <code>EditorKit</code> will be loaded with <code>Class.forName</code>
  912. * when registered with this method.
  913. *
  914. * @param type the non-<code>null</code> content type
  915. * @param classname the class to load later
  916. */
  917. public static void registerEditorKitForContentType(String type, String classname) {
  918. getKitLoaderRegistry().remove(type);
  919. getKitTypeRegistry().put(type, classname);
  920. getKitRegisty().remove(type);
  921. }
  922. /**
  923. * Establishes the default bindings of <code>type</code> to
  924. * <code>classname</code>.
  925. * The class will be dynamically loaded later when actually
  926. * needed using the given <code>ClassLoader</code>,
  927. * and can be safely changed
  928. * before attempted uses to avoid loading unwanted classes.
  929. *
  930. * @param type the non-</code>null</code> content type
  931. * @param classname the class to load later
  932. * @param loader the <code>ClassLoader</code> to use to load the name
  933. */
  934. public static void registerEditorKitForContentType(String type, String classname, ClassLoader loader) {
  935. getKitTypeRegistry().put(type, classname);
  936. getKitLoaderRegistry().put(type, loader);
  937. getKitRegisty().remove(type);
  938. }
  939. /**
  940. * Returns the currently registered <code>EditorKit</code>
  941. * class name for the type <code>type</code>.
  942. *
  943. * @param type the non-<code>null</code> content type
  944. *
  945. * @since 1.3
  946. */
  947. public static String getEditorKitClassNameForContentType(String type) {
  948. return (String)getKitTypeRegistry().get(type);
  949. }
  950. private static Hashtable getKitTypeRegistry() {
  951. loadDefaultKitsIfNecessary();
  952. return (Hashtable)SwingUtilities.appContextGet(kitTypeRegistryKey);
  953. }
  954. private static Hashtable getKitLoaderRegistry() {
  955. loadDefaultKitsIfNecessary();
  956. return (Hashtable)SwingUtilities.appContextGet(kitLoaderRegistryKey);
  957. }
  958. private static Hashtable getKitRegisty() {
  959. Hashtable ht = (Hashtable)SwingUtilities.appContextGet(kitRegistryKey);
  960. if (ht == null) {
  961. ht = new Hashtable(3);
  962. SwingUtilities.appContextPut(kitRegistryKey, ht);
  963. }
  964. return ht;
  965. }
  966. /**
  967. * This is invoked every time the registries are accessed. Loading
  968. * is done this way instead of via a static as the static is only
  969. * called once when running in plugin resulting in the entries only
  970. * appearing in the first applet.
  971. */
  972. private static void loadDefaultKitsIfNecessary() {
  973. if (SwingUtilities.appContextGet(kitTypeRegistryKey) == null) {
  974. Hashtable ht = new Hashtable();
  975. SwingUtilities.appContextPut(kitTypeRegistryKey, ht);
  976. ht = new Hashtable();
  977. SwingUtilities.appContextPut(kitLoaderRegistryKey, ht);
  978. registerEditorKitForContentType("text/plain",
  979. "javax.swing.JEditorPane$PlainEditorKit");
  980. registerEditorKitForContentType("text/html",
  981. "javax.swing.text.html.HTMLEditorKit");
  982. registerEditorKitForContentType("text/rtf",
  983. "javax.swing.text.rtf.RTFEditorKit");
  984. registerEditorKitForContentType("application/rtf",
  985. "javax.swing.text.rtf.RTFEditorKit");
  986. }
  987. }
  988. // --- java.awt.Component methods --------------------------
  989. /**
  990. * Returns the preferred size for the <code>JEditorPane</code>.
  991. * The preferred size for <code>JEditorPane</code> is slightly altered
  992. * from the preferred size of the superclass. If the size
  993. * of the viewport has become smaller than the minimum size
  994. * of the component, the scrollable definition for tracking
  995. * width or height will turn to false. The default viewport
  996. * layout will give the preferred size, and that is not desired
  997. * in the case where the scrollable is tracking. In that case
  998. * the <em>normal</em> preferred size is adjusted to the
  999. * minimum size. This allows things like HTML tables to
  1000. * shrink down to their minimum size and then be laid out at
  1001. * their minimum size, refusing to shrink any further.
  1002. *
  1003. * @return a <code>Dimension</code> containing the preferred size
  1004. */
  1005. public Dimension getPreferredSize() {
  1006. Dimension d = super.getPreferredSize();
  1007. if (getParent() instanceof JViewport) {
  1008. JViewport port = (JViewport)getParent();
  1009. TextUI ui = getUI();
  1010. if (! getScrollableTracksViewportWidth()) {
  1011. int w = port.getWidth();
  1012. Dimension min = ui.getMinimumSize(this);
  1013. if (w < min.width) {
  1014. d.width = min.width;
  1015. }
  1016. }
  1017. if (! getScrollableTracksViewportHeight()) {
  1018. int h = port.getHeight();
  1019. Dimension min = ui.getMinimumSize(this);
  1020. if (h < min.height) {
  1021. d.height = min.height;
  1022. }
  1023. }
  1024. }
  1025. return d;
  1026. }
  1027. // --- JComponent methods ---------------------------------
  1028. /**
  1029. * Turns off tab traversal once focus is gained.
  1030. *
  1031. * @return true, to indicate that the focus is being managed;
  1032. * or false otherwise
  1033. */
  1034. public boolean isManagingFocus() {
  1035. return managingFocus;
  1036. }
  1037. /**
  1038. * Makes <code>JEditorPane</code> be the root of a focus cycle.
  1039. * This means that, by default, tabbing within the editor
  1040. * pane will move between components of the pane, such as
  1041. * form controls, but not out of the pane.
  1042. * <p>Note that this method always returns true.
  1043. *
  1044. * @see JComponent#isFocusCycleRoot
  1045. * @return true
  1046. */
  1047. public boolean isFocusCycleRoot() {
  1048. return true;
  1049. }
  1050. /**
  1051. * Overridden to handle processing of tab/shift tab. If <code>e</code>
  1052. * identifies a tab, the editor pane is not editable and has components,
  1053. * then the <code>FocusManager</code> is asked to pass focus to the
  1054. * next/previous component.
  1055. *
  1056. * @param e the current key event
  1057. */
  1058. protected void processComponentKeyEvent(KeyEvent e) {
  1059. super.processComponentKeyEvent(e);
  1060. if (isManagingFocus()) {
  1061. if ((e.getKeyCode() == KeyEvent.VK_TAB || e.getKeyChar() == '\t')) {
  1062. if (e.getID() == KeyEvent.KEY_PRESSED && !isEditable() &&
  1063. getComponentCount() > 0) {
  1064. // We aren't editable, and have child components, which
  1065. // are likely form elements. By setting managingFocus to
  1066. // false the FocusManager will look at our children and
  1067. // give one of them focus.
  1068. managingFocus = false;
  1069. try {
  1070. if ((e.getModifiers() & ActionEvent.SHIFT_MASK) ==
  1071. ActionEvent.SHIFT_MASK) {
  1072. FocusManager.getCurrentManager().
  1073. focusPreviousComponent(this);
  1074. }
  1075. else {
  1076. FocusManager.getCurrentManager().
  1077. focusNextComponent(this);
  1078. }
  1079. e.consume();
  1080. }
  1081. finally {
  1082. managingFocus = true;
  1083. }
  1084. }
  1085. }
  1086. }
  1087. }
  1088. /**
  1089. * Make sure that TAB and Shift-TAB events get consumed, so that
  1090. * awt doesn't attempt focus traversal.
  1091. *
  1092. * @param e the current key event
  1093. */
  1094. protected void processKeyEvent(KeyEvent e) {
  1095. super.processKeyEvent(e);
  1096. // tab consumption
  1097. // We are actually consuming any TABs modified in any way, because
  1098. // we don't want awt to get anything it can use for focus traversal.
  1099. if (isManagingFocus()) {
  1100. if ((e.getKeyCode() == KeyEvent.VK_TAB || e.getKeyChar() == '\t')) {
  1101. e.consume();
  1102. }
  1103. }
  1104. }
  1105. // --- JTextComponent methods -----------------------------
  1106. /**
  1107. * Sets the text of this <code>TextComponent</code> to the specified
  1108. * content,
  1109. * which is expected to be in the format of the content type of
  1110. * this editor. For example, if the type is set to <code>text/html</code>
  1111. * the string should be specified in terms of HTML.
  1112. * <p>
  1113. * This is implemented to remove the contents of the current document,
  1114. * and replace them by parsing the given string using the current
  1115. * <code>EditorKit</code>. This gives the semantics of the
  1116. * superclass by not changing
  1117. * out the model, while supporting the content type currently set on
  1118. * this component. The assumption is that the previous content is
  1119. * relatively
  1120. * small, and that the previous content doesn't have side effects.
  1121. * Both of those assumptions can be violated and cause undesirable results.
  1122. * To avoid this, create a new document,
  1123. * <code>getEditorKit().createDefaultDocument()</code>, and replace the
  1124. * existing <code>Document</code> with the new one. You are then assured the
  1125. * previous <code>Document</code> won't have any lingering state.
  1126. * <ol>
  1127. * <li>
  1128. * Leaving the existing model in place means that the old view will be
  1129. * torn down, and a new view created, where replacing the document would
  1130. * avoid the tear down of the old view.
  1131. * <li>
  1132. * Some formats (such as HTML) can install things into the document that
  1133. * can influence future contents. HTML can have style information embedded
  1134. * that would influence the next content installed unexpectedly.
  1135. * </ol>
  1136. * <p>
  1137. * An alternative way to load this component with a string would be to
  1138. * create a StringReader and call the read method. In this case the model
  1139. * would be replaced after it was initialized with the contents of the
  1140. * string.
  1141. * <p>
  1142. * This method is thread safe, although most Swing methods
  1143. * are not. Please see
  1144. * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  1145. * and Swing</A> for more information.
  1146. *
  1147. * @param t the new text to be set
  1148. * @see #getText
  1149. * @beaninfo
  1150. * description: the text of this component
  1151. */
  1152. public void setText(String t) {
  1153. try {
  1154. Document doc = getDocument();
  1155. doc.remove(0, doc.getLength());
  1156. Reader r = new StringReader(t);
  1157. EditorKit kit = getEditorKit();
  1158. kit.read(r, doc, 0);
  1159. } catch (IOException ioe) {
  1160. getToolkit().beep();
  1161. } catch (BadLocationException ble) {
  1162. getToolkit().beep();
  1163. }
  1164. }
  1165. /**
  1166. * Returns the text contained in this <code>TextComponent</code>
  1167. * in terms of the
  1168. * content type of this editor. If an exception is thrown while
  1169. * attempting to retrieve the text, <code>null</code> will be returned.
  1170. * This is implemented to call <code>JTextComponent.write</code> with
  1171. * a <code>StringWriter</code>.
  1172. *
  1173. * @return the text
  1174. * @see #setText
  1175. */
  1176. public String getText() {
  1177. String txt;
  1178. try {
  1179. StringWriter buf = new StringWriter();
  1180. write(buf);
  1181. txt = buf.toString();
  1182. } catch (IOException ioe) {
  1183. txt = null;
  1184. }
  1185. return txt;
  1186. }
  1187. // --- Scrollable ----------------------------------------
  1188. /**
  1189. * Returns true if a viewport should always force the width of this
  1190. * <code>Scrollable</code> to match the width of the viewport.
  1191. *
  1192. * @return true if a viewport should force the Scrollables width to
  1193. * match its own, false otherwise
  1194. */
  1195. public boolean getScrollableTracksViewportWidth() {
  1196. if (getParent() instanceof JViewport) {
  1197. JViewport port = (JViewport)getParent();
  1198. TextUI ui = getUI();
  1199. int w = port.getWidth();
  1200. Dimension min = ui.getMinimumSize(this);
  1201. Dimension max = ui.getMaximumSize(this);
  1202. if ((w >= min.width) && (w <= max.width)) {
  1203. return true;
  1204. }
  1205. }
  1206. return false;
  1207. }
  1208. /**
  1209. * Returns true if a viewport should always force the height of this
  1210. * <code>Scrollable</code> to match the height of the viewport.
  1211. *
  1212. * @return true if a viewport should force the
  1213. * <code>Scrollable</code>'s height to match its own,
  1214. * false otherwise
  1215. */
  1216. public boolean getScrollableTracksViewportHeight() {
  1217. if (getParent() instanceof JViewport) {
  1218. JViewport port = (JViewport)getParent();
  1219. TextUI ui = getUI();
  1220. int h = port.getHeight();
  1221. Dimension min = ui.getMinimumSize(this);
  1222. if (h >= min.height) {
  1223. Dimension max = ui.getMaximumSize(this);
  1224. if (h <= max.height) {
  1225. return true;
  1226. }
  1227. }
  1228. }
  1229. return false;
  1230. }
  1231. // --- Serialization ------------------------------------
  1232. /**
  1233. * See <code>readObject</code> and <code>writeObject</code> in
  1234. * <code>JComponent</code> for more
  1235. * information about serialization in Swing.
  1236. */
  1237. private void writeObject(ObjectOutputStream s) throws IOException {
  1238. s.defaultWriteObject();
  1239. if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  1240. ui.installUI(this);
  1241. }
  1242. }
  1243. // --- variables ---------------------------------------
  1244. /**
  1245. * Current content binding of the editor.
  1246. */
  1247. private EditorKit kit;
  1248. private Hashtable pageProperties;
  1249. /**
  1250. * Table of registered type handlers for this editor.
  1251. */
  1252. private Hashtable typeHandlers;
  1253. /**
  1254. * Indicates whether we are managing focus.
  1255. * @see #processComponentKeyEvent
  1256. * @see #isManagingFocus
  1257. */
  1258. private boolean managingFocus = true;
  1259. /*
  1260. * Private AppContext keys for this class's static variables.
  1261. */
  1262. private static final Object kitRegistryKey =
  1263. new StringBuffer("JEditorPane.kitRegistry");
  1264. private static final Object kitTypeRegistryKey =
  1265. new StringBuffer("JEditorPane.kitTypeRegistry");
  1266. private static final Object kitLoaderRegistryKey =
  1267. new StringBuffer("JEditorPane.kitLoaderRegistry");
  1268. /**
  1269. * @see #getUIClassID
  1270. * @see #readObject
  1271. */
  1272. private static final String uiClassID = "EditorPaneUI";
  1273. /**
  1274. * Returns a string representation of this <code>JEditorPane</code>.
  1275. * This method
  1276. * is intended to be used only for debugging purposes, and the
  1277. * content and format of the returned string may vary between
  1278. * implementations. The returned string may be empty but may not
  1279. * be <code>null</code>.
  1280. *
  1281. * @return a string representation of this <code>JEditorPane</code>
  1282. */
  1283. protected String paramString() {
  1284. String kitString = (kit != null ?
  1285. kit.toString() : "");
  1286. String typeHandlersString = (typeHandlers != null ?
  1287. typeHandlers.toString() : "");
  1288. return super.paramString() +
  1289. ",kit=" + kitString +
  1290. ",typeHandlers=" + typeHandlersString;
  1291. }
  1292. /////////////////
  1293. // Accessibility support
  1294. ////////////////
  1295. /**
  1296. * Gets the AccessibleContext associated with this JEditorPane.
  1297. * For editor panes, the AccessibleContext takes the form of an
  1298. * AccessibleJEditorPane.
  1299. * A new AccessibleJEditorPane instance is created if necessary.
  1300. *
  1301. * @return an AccessibleJEditorPane that serves as the
  1302. * AccessibleContext of this JEditorPane
  1303. */
  1304. public AccessibleContext getAccessibleContext() {
  1305. if (accessibleContext == null) {
  1306. if (JEditorPane.this.getEditorKit() instanceof HTMLEditorKit) {
  1307. accessibleContext = new AccessibleJEditorPaneHTML();
  1308. } else {
  1309. accessibleContext = new AccessibleJEditorPane();
  1310. }
  1311. }
  1312. return accessibleContext;
  1313. }
  1314. /**
  1315. * This class implements accessibility support for the
  1316. * <code>JEditorPane</code> class. It provides an implementation of the
  1317. * Java Accessibility API appropriate to editor pane user-interface
  1318. * elements.
  1319. * <p>
  1320. * <strong>Warning:</strong>
  1321. * Serialized objects of this class will not be compatible with
  1322. * future Swing releases. The current serialization support is appropriate
  1323. * for short term storage or RMI between applications running the same
  1324. * version of Swing. A future release of Swing will provide support for
  1325. * long term persistence.
  1326. */
  1327. protected class AccessibleJEditorPane extends AccessibleJTextComponent {
  1328. /**
  1329. * Gets the accessibleDescription property of this object. If this
  1330. * property isn't set, return the content type of this JEditorPane
  1331. * instead (e.g. "plain/text", "html/text", etc.
  1332. *
  1333. * @return the localized description of the object; null if
  1334. * this object does not have a description
  1335. *
  1336. * @see #setAccessibleName
  1337. */
  1338. public String getAccessibleDescription() {
  1339. if (accessibleDescription != null) {
  1340. return accessibleDescription;
  1341. } else {
  1342. return JEditorPane.this.getContentType();
  1343. }
  1344. }
  1345. /**
  1346. * Gets the state set of this object.
  1347. *
  1348. * @return an instance of AccessibleStateSet describing the states
  1349. * of the object
  1350. * @see AccessibleStateSet
  1351. */
  1352. public AccessibleStateSet getAccessibleStateSet() {
  1353. AccessibleStateSet states = super.getAccessibleStateSet();
  1354. states.add(AccessibleState.MULTI_LINE);
  1355. return states;
  1356. }
  1357. }
  1358. /**
  1359. * This class provides support for <code>AccessibleHypertext</code>,
  1360. * and is used in instances where the <code>EditorKit</code>
  1361. * installed in this <code>JEditorPane</code> is an instance of
  1362. * <code>HTMLEditorKit</code>.
  1363. * <p>
  1364. * <strong>Warning:</strong>
  1365. * Serialized objects of this class will not be compatible with
  1366. * future Swing releases. The current serialization support is appropriate
  1367. * for short term storage or RMI between applications running the same
  1368. * version of Swing. A future release of Swing will provide support for
  1369. * baseline for serialized form of Swing objects.
  1370. */
  1371. protected class AccessibleJEditorPaneHTML extends AccessibleJEditorPane {
  1372. public AccessibleText getAccessibleText() {
  1373. return new JEditorPaneAccessibleHypertextSupport();
  1374. }
  1375. }
  1376. /**
  1377. * What's returned by
  1378. * <code>AccessibleJEditorPaneHTML.getAccessibleText</code>.
  1379. *
  1380. * Provid