143 * by the setting of the StyleSheet property on the EditorKit.
144 *
145 * <dt>
146 * Not lossy
147 * <dd>
148 * An EditorKit has the ability to be read and save documents.
149 * It is generally the most pleasing to users if there is no loss
150 * of data between the two operation. The policy of the HTMLEditorKit
151 * will be to store things not recognized or not necessarily visible
152 * so they can be subsequently written out. The model of the HTML document
153 * should therefore contain all information discovered while reading the
154 * document. This is constrained in some ways by the need to support
155 * editing (i.e. incorrect documents sometimes must be normalized).
156 * The guiding principle is that information shouldn't be lost, but
157 * some might be synthesized to produce a more correct model or it might
158 * be rearranged.
159 * </dl>
160 *
161 * @author Timothy Prinzing
162 */
163 public class HTMLEditorKit extends StyledEditorKit implements Accessible {
164
165 private JEditorPane theEditor;
166
167 /**
168 * Constructs an HTMLEditorKit, creates a StyleContext,
169 * and loads the style sheet.
170 */
171 public HTMLEditorKit() {
172
173 }
174
175 /**
176 * Get the MIME type of the data that this
177 * kit represents support for. This kit supports
178 * the type <code>text/html</code>.
179 *
180 * @return the type
181 */
182 public String getContentType() {
623 private static final Cursor MoveCursor = Cursor.getPredefinedCursor
624 (Cursor.HAND_CURSOR);
625 private static final Cursor DefaultCursor = Cursor.getPredefinedCursor
626 (Cursor.DEFAULT_CURSOR);
627
628 /** Shared factory for creating HTML Views. */
629 private static final ViewFactory defaultFactory = new HTMLFactory();
630
631 MutableAttributeSet input;
632 private static final Object DEFAULT_STYLES_KEY = new Object();
633 private LinkController linkHandler = new LinkController();
634 private static Parser defaultParser = null;
635 private Cursor defaultCursor = DefaultCursor;
636 private Cursor linkCursor = MoveCursor;
637 private boolean isAutoFormSubmission = true;
638
639 /**
640 * Class to watch the associated component and fire
641 * hyperlink events on it when appropriate.
642 */
643 public static class LinkController extends MouseAdapter implements MouseMotionListener, Serializable {
644 private Element curElem = null;
645 /**
646 * If true, the current element (curElem) represents an image.
647 */
648 private boolean curElemImage = false;
649 private String href = null;
650 /** This is used by viewToModel to avoid allocing a new array each
651 * time. */
652 private transient Position.Bias[] bias = new Position.Bias[1];
653 /**
654 * Current offset.
655 */
656 private int curOffset;
657
658 /**
659 * Called for a mouse click event.
660 * If the component is read-only (ie a browser) then
661 * the clicked event is used to drive an attempt to
662 * follow the reference specified by a link.
1465 HTML.Tag.BODY, HTML.Tag.PRE),
1466 nextLinkAction, previousLinkAction, activateLinkAction,
1467
1468 new BeginAction(beginAction, false),
1469 new BeginAction(selectionBeginAction, true)
1470 };
1471
1472 // link navigation support
1473 private boolean foundLink = false;
1474 private int prevHypertextOffset = -1;
1475 private Object linkNavigationTag;
1476
1477
1478 /**
1479 * An abstract Action providing some convenience methods that may
1480 * be useful in inserting HTML into an existing document.
1481 * <p>NOTE: None of the convenience methods obtain a lock on the
1482 * document. If you have another thread modifying the text these
1483 * methods may have inconsistent behavior, or return the wrong thing.
1484 */
1485 public static abstract class HTMLTextAction extends StyledTextAction {
1486 public HTMLTextAction(String name) {
1487 super(name);
1488 }
1489
1490 /**
1491 * @return HTMLDocument of <code>e</code>.
1492 */
1493 protected HTMLDocument getHTMLDocument(JEditorPane e) {
1494 Document d = e.getDocument();
1495 if (d instanceof HTMLDocument) {
1496 return (HTMLDocument) d;
1497 }
1498 throw new IllegalArgumentException("document must be HTMLDocument");
1499 }
1500
1501 /**
1502 * @return HTMLEditorKit for <code>e</code>.
1503 */
1504 protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) {
1575 }
1576
1577
1578 /**
1579 * InsertHTMLTextAction can be used to insert an arbitrary string of HTML
1580 * into an existing HTML document. At least two HTML.Tags need to be
1581 * supplied. The first Tag, parentTag, identifies the parent in
1582 * the document to add the elements to. The second tag, addTag,
1583 * identifies the first tag that should be added to the document as
1584 * seen in the HTML string. One important thing to remember, is that
1585 * the parser is going to generate all the appropriate tags, even if
1586 * they aren't in the HTML string passed in.<p>
1587 * For example, lets say you wanted to create an action to insert
1588 * a table into the body. The parentTag would be HTML.Tag.BODY,
1589 * addTag would be HTML.Tag.TABLE, and the string could be something
1590 * like <table><tr><td></td></tr></table>.
1591 * <p>There is also an option to supply an alternate parentTag and
1592 * addTag. These will be checked for if there is no parentTag at
1593 * offset.
1594 */
1595 public static class InsertHTMLTextAction extends HTMLTextAction {
1596 public InsertHTMLTextAction(String name, String html,
1597 HTML.Tag parentTag, HTML.Tag addTag) {
1598 this(name, html, parentTag, addTag, null, null);
1599 }
1600
1601 public InsertHTMLTextAction(String name, String html,
1602 HTML.Tag parentTag,
1603 HTML.Tag addTag,
1604 HTML.Tag alternateParentTag,
1605 HTML.Tag alternateAddTag) {
1606 this(name, html, parentTag, addTag, alternateParentTag,
1607 alternateAddTag, true);
1608 }
1609
1610 /* public */
1611 InsertHTMLTextAction(String name, String html,
1612 HTML.Tag parentTag,
1613 HTML.Tag addTag,
1614 HTML.Tag alternateParentTag,
1806 /** Tag to check for in the document. */
1807 protected HTML.Tag parentTag;
1808 /** Tag in HTML to start adding tags from. */
1809 protected HTML.Tag addTag;
1810 /** Alternate Tag to check for in the document if parentTag is
1811 * not found. */
1812 protected HTML.Tag alternateParentTag;
1813 /** Alternate tag in HTML to start adding tags from if parentTag
1814 * is not found and alternateParentTag is found. */
1815 protected HTML.Tag alternateAddTag;
1816 /** True indicates the selection should be adjusted after an insert. */
1817 boolean adjustSelection;
1818 }
1819
1820
1821 /**
1822 * InsertHRAction is special, at actionPerformed time it will determine
1823 * the parent HTML.Tag based on the paragraph element at the selection
1824 * start.
1825 */
1826 static class InsertHRAction extends InsertHTMLTextAction {
1827 InsertHRAction() {
1828 super("InsertHR", "<hr>", null, HTML.Tag.IMPLIED, null, null,
1829 false);
1830 }
1831
1832 /**
1833 * Inserts the HTML into the document.
1834 *
1835 * @param ae the event
1836 */
1837 public void actionPerformed(ActionEvent ae) {
1838 JEditorPane editor = getEditor(ae);
1839 if (editor != null) {
1840 HTMLDocument doc = getHTMLDocument(editor);
1841 int offset = editor.getSelectionStart();
1842 Element paragraph = doc.getParagraphElement(offset);
1843 if (paragraph.getParentElement() != null) {
1844 parentTag = (HTML.Tag)paragraph.getParentElement().
1845 getAttributes().getAttribute
1860 Object nextKey = names.nextElement();
1861 Object nextVal = attr.getAttribute(nextKey);
1862 if (nextVal instanceof AttributeSet) {
1863 Object value = getAttrValue((AttributeSet)nextVal, key);
1864 if (value != null) {
1865 return value;
1866 }
1867 } else if (nextKey == key) {
1868 return nextVal;
1869 }
1870 }
1871 return null;
1872 }
1873
1874 /*
1875 * Action to move the focus on the next or previous hypertext link
1876 * or object. TODO: This method relies on support from the
1877 * javax.accessibility package. The text package should support
1878 * keyboard navigation of text elements directly.
1879 */
1880 static class NavigateLinkAction extends TextAction implements CaretListener {
1881
1882 private static final FocusHighlightPainter focusPainter =
1883 new FocusHighlightPainter(null);
1884 private final boolean focusBack;
1885
1886 /*
1887 * Create this action with the appropriate identifier.
1888 */
1889 public NavigateLinkAction(String actionName) {
1890 super(actionName);
1891 focusBack = "previous-link-action".equals(actionName);
1892 }
1893
1894 /**
1895 * Called when the caret position is updated.
1896 *
1897 * @param e the caret event
1898 */
1899 public void caretUpdate(CaretEvent e) {
2069 Rectangle r = (shape instanceof Rectangle) ?
2070 (Rectangle)shape : shape.getBounds();
2071 g.drawRect(r.x, r.y, r.width - 1, r.height);
2072 return r;
2073 } catch (BadLocationException e) {
2074 // can't render
2075 }
2076 }
2077 // Only if exception
2078 return null;
2079 }
2080 }
2081 }
2082
2083 /*
2084 * Action to activate the hypertext link that has focus.
2085 * TODO: This method relies on support from the
2086 * javax.accessibility package. The text package should support
2087 * keyboard navigation of text elements directly.
2088 */
2089 static class ActivateLinkAction extends TextAction {
2090
2091 /**
2092 * Create this action with the appropriate identifier.
2093 */
2094 public ActivateLinkAction(String actionName) {
2095 super(actionName);
2096 }
2097
2098 /*
2099 * activates the hyperlink at offset
2100 */
2101 private void activateLink(String href, HTMLDocument doc,
2102 JEditorPane editor, int offset) {
2103 try {
2104 URL page =
2105 (URL)doc.getProperty(Document.StreamDescriptionProperty);
2106 URL url = new URL(page, href);
2107 HyperlinkEvent linkEvent = new HyperlinkEvent
2108 (editor, HyperlinkEvent.EventType.
2242 }
2243 }
2244 }
2245
2246 private static int getBodyElementStart(JTextComponent comp) {
2247 Element rootElement = comp.getDocument().getRootElements()[0];
2248 for (int i = 0; i < rootElement.getElementCount(); i++) {
2249 Element currElement = rootElement.getElement(i);
2250 if("body".equals(currElement.getName())) {
2251 return currElement.getStartOffset();
2252 }
2253 }
2254 return 0;
2255 }
2256
2257 /*
2258 * Move the caret to the beginning of the document.
2259 * @see DefaultEditorKit#beginAction
2260 * @see HTMLEditorKit#getActions
2261 */
2262
2263 static class BeginAction extends TextAction {
2264
2265 /* Create this object with the appropriate identifier. */
2266 BeginAction(String nm, boolean select) {
2267 super(nm);
2268 this.select = select;
2269 }
2270
2271 /** The operation to perform when this action is triggered. */
2272 public void actionPerformed(ActionEvent e) {
2273 JTextComponent target = getTextComponent(e);
2274 int bodyStart = getBodyElementStart(target);
2275
2276 if (target != null) {
2277 if (select) {
2278 target.moveCaretPosition(bodyStart);
2279 } else {
2280 target.setCaretPosition(bodyStart);
2281 }
2282 }
|
143 * by the setting of the StyleSheet property on the EditorKit.
144 *
145 * <dt>
146 * Not lossy
147 * <dd>
148 * An EditorKit has the ability to be read and save documents.
149 * It is generally the most pleasing to users if there is no loss
150 * of data between the two operation. The policy of the HTMLEditorKit
151 * will be to store things not recognized or not necessarily visible
152 * so they can be subsequently written out. The model of the HTML document
153 * should therefore contain all information discovered while reading the
154 * document. This is constrained in some ways by the need to support
155 * editing (i.e. incorrect documents sometimes must be normalized).
156 * The guiding principle is that information shouldn't be lost, but
157 * some might be synthesized to produce a more correct model or it might
158 * be rearranged.
159 * </dl>
160 *
161 * @author Timothy Prinzing
162 */
163 @SuppressWarnings("serial") // Same-version serialization only
164 public class HTMLEditorKit extends StyledEditorKit implements Accessible {
165
166 private JEditorPane theEditor;
167
168 /**
169 * Constructs an HTMLEditorKit, creates a StyleContext,
170 * and loads the style sheet.
171 */
172 public HTMLEditorKit() {
173
174 }
175
176 /**
177 * Get the MIME type of the data that this
178 * kit represents support for. This kit supports
179 * the type <code>text/html</code>.
180 *
181 * @return the type
182 */
183 public String getContentType() {
624 private static final Cursor MoveCursor = Cursor.getPredefinedCursor
625 (Cursor.HAND_CURSOR);
626 private static final Cursor DefaultCursor = Cursor.getPredefinedCursor
627 (Cursor.DEFAULT_CURSOR);
628
629 /** Shared factory for creating HTML Views. */
630 private static final ViewFactory defaultFactory = new HTMLFactory();
631
632 MutableAttributeSet input;
633 private static final Object DEFAULT_STYLES_KEY = new Object();
634 private LinkController linkHandler = new LinkController();
635 private static Parser defaultParser = null;
636 private Cursor defaultCursor = DefaultCursor;
637 private Cursor linkCursor = MoveCursor;
638 private boolean isAutoFormSubmission = true;
639
640 /**
641 * Class to watch the associated component and fire
642 * hyperlink events on it when appropriate.
643 */
644 @SuppressWarnings("serial") // Same-version serialization only
645 public static class LinkController extends MouseAdapter implements MouseMotionListener, Serializable {
646 private Element curElem = null;
647 /**
648 * If true, the current element (curElem) represents an image.
649 */
650 private boolean curElemImage = false;
651 private String href = null;
652 /** This is used by viewToModel to avoid allocing a new array each
653 * time. */
654 private transient Position.Bias[] bias = new Position.Bias[1];
655 /**
656 * Current offset.
657 */
658 private int curOffset;
659
660 /**
661 * Called for a mouse click event.
662 * If the component is read-only (ie a browser) then
663 * the clicked event is used to drive an attempt to
664 * follow the reference specified by a link.
1467 HTML.Tag.BODY, HTML.Tag.PRE),
1468 nextLinkAction, previousLinkAction, activateLinkAction,
1469
1470 new BeginAction(beginAction, false),
1471 new BeginAction(selectionBeginAction, true)
1472 };
1473
1474 // link navigation support
1475 private boolean foundLink = false;
1476 private int prevHypertextOffset = -1;
1477 private Object linkNavigationTag;
1478
1479
1480 /**
1481 * An abstract Action providing some convenience methods that may
1482 * be useful in inserting HTML into an existing document.
1483 * <p>NOTE: None of the convenience methods obtain a lock on the
1484 * document. If you have another thread modifying the text these
1485 * methods may have inconsistent behavior, or return the wrong thing.
1486 */
1487 @SuppressWarnings("serial") // Superclass is not serializable across versions
1488 public static abstract class HTMLTextAction extends StyledTextAction {
1489 public HTMLTextAction(String name) {
1490 super(name);
1491 }
1492
1493 /**
1494 * @return HTMLDocument of <code>e</code>.
1495 */
1496 protected HTMLDocument getHTMLDocument(JEditorPane e) {
1497 Document d = e.getDocument();
1498 if (d instanceof HTMLDocument) {
1499 return (HTMLDocument) d;
1500 }
1501 throw new IllegalArgumentException("document must be HTMLDocument");
1502 }
1503
1504 /**
1505 * @return HTMLEditorKit for <code>e</code>.
1506 */
1507 protected HTMLEditorKit getHTMLEditorKit(JEditorPane e) {
1578 }
1579
1580
1581 /**
1582 * InsertHTMLTextAction can be used to insert an arbitrary string of HTML
1583 * into an existing HTML document. At least two HTML.Tags need to be
1584 * supplied. The first Tag, parentTag, identifies the parent in
1585 * the document to add the elements to. The second tag, addTag,
1586 * identifies the first tag that should be added to the document as
1587 * seen in the HTML string. One important thing to remember, is that
1588 * the parser is going to generate all the appropriate tags, even if
1589 * they aren't in the HTML string passed in.<p>
1590 * For example, lets say you wanted to create an action to insert
1591 * a table into the body. The parentTag would be HTML.Tag.BODY,
1592 * addTag would be HTML.Tag.TABLE, and the string could be something
1593 * like <table><tr><td></td></tr></table>.
1594 * <p>There is also an option to supply an alternate parentTag and
1595 * addTag. These will be checked for if there is no parentTag at
1596 * offset.
1597 */
1598 @SuppressWarnings("serial") // Superclass is not serializable across versions
1599 public static class InsertHTMLTextAction extends HTMLTextAction {
1600 public InsertHTMLTextAction(String name, String html,
1601 HTML.Tag parentTag, HTML.Tag addTag) {
1602 this(name, html, parentTag, addTag, null, null);
1603 }
1604
1605 public InsertHTMLTextAction(String name, String html,
1606 HTML.Tag parentTag,
1607 HTML.Tag addTag,
1608 HTML.Tag alternateParentTag,
1609 HTML.Tag alternateAddTag) {
1610 this(name, html, parentTag, addTag, alternateParentTag,
1611 alternateAddTag, true);
1612 }
1613
1614 /* public */
1615 InsertHTMLTextAction(String name, String html,
1616 HTML.Tag parentTag,
1617 HTML.Tag addTag,
1618 HTML.Tag alternateParentTag,
1810 /** Tag to check for in the document. */
1811 protected HTML.Tag parentTag;
1812 /** Tag in HTML to start adding tags from. */
1813 protected HTML.Tag addTag;
1814 /** Alternate Tag to check for in the document if parentTag is
1815 * not found. */
1816 protected HTML.Tag alternateParentTag;
1817 /** Alternate tag in HTML to start adding tags from if parentTag
1818 * is not found and alternateParentTag is found. */
1819 protected HTML.Tag alternateAddTag;
1820 /** True indicates the selection should be adjusted after an insert. */
1821 boolean adjustSelection;
1822 }
1823
1824
1825 /**
1826 * InsertHRAction is special, at actionPerformed time it will determine
1827 * the parent HTML.Tag based on the paragraph element at the selection
1828 * start.
1829 */
1830 @SuppressWarnings("serial") // Superclass is not serializable across versions
1831 static class InsertHRAction extends InsertHTMLTextAction {
1832 InsertHRAction() {
1833 super("InsertHR", "<hr>", null, HTML.Tag.IMPLIED, null, null,
1834 false);
1835 }
1836
1837 /**
1838 * Inserts the HTML into the document.
1839 *
1840 * @param ae the event
1841 */
1842 public void actionPerformed(ActionEvent ae) {
1843 JEditorPane editor = getEditor(ae);
1844 if (editor != null) {
1845 HTMLDocument doc = getHTMLDocument(editor);
1846 int offset = editor.getSelectionStart();
1847 Element paragraph = doc.getParagraphElement(offset);
1848 if (paragraph.getParentElement() != null) {
1849 parentTag = (HTML.Tag)paragraph.getParentElement().
1850 getAttributes().getAttribute
1865 Object nextKey = names.nextElement();
1866 Object nextVal = attr.getAttribute(nextKey);
1867 if (nextVal instanceof AttributeSet) {
1868 Object value = getAttrValue((AttributeSet)nextVal, key);
1869 if (value != null) {
1870 return value;
1871 }
1872 } else if (nextKey == key) {
1873 return nextVal;
1874 }
1875 }
1876 return null;
1877 }
1878
1879 /*
1880 * Action to move the focus on the next or previous hypertext link
1881 * or object. TODO: This method relies on support from the
1882 * javax.accessibility package. The text package should support
1883 * keyboard navigation of text elements directly.
1884 */
1885 @SuppressWarnings("serial") // Superclass is not serializable across versions
1886 static class NavigateLinkAction extends TextAction implements CaretListener {
1887
1888 private static final FocusHighlightPainter focusPainter =
1889 new FocusHighlightPainter(null);
1890 private final boolean focusBack;
1891
1892 /*
1893 * Create this action with the appropriate identifier.
1894 */
1895 public NavigateLinkAction(String actionName) {
1896 super(actionName);
1897 focusBack = "previous-link-action".equals(actionName);
1898 }
1899
1900 /**
1901 * Called when the caret position is updated.
1902 *
1903 * @param e the caret event
1904 */
1905 public void caretUpdate(CaretEvent e) {
2075 Rectangle r = (shape instanceof Rectangle) ?
2076 (Rectangle)shape : shape.getBounds();
2077 g.drawRect(r.x, r.y, r.width - 1, r.height);
2078 return r;
2079 } catch (BadLocationException e) {
2080 // can't render
2081 }
2082 }
2083 // Only if exception
2084 return null;
2085 }
2086 }
2087 }
2088
2089 /*
2090 * Action to activate the hypertext link that has focus.
2091 * TODO: This method relies on support from the
2092 * javax.accessibility package. The text package should support
2093 * keyboard navigation of text elements directly.
2094 */
2095 @SuppressWarnings("serial") // Superclass is not serializable across versions
2096 static class ActivateLinkAction extends TextAction {
2097
2098 /**
2099 * Create this action with the appropriate identifier.
2100 */
2101 public ActivateLinkAction(String actionName) {
2102 super(actionName);
2103 }
2104
2105 /*
2106 * activates the hyperlink at offset
2107 */
2108 private void activateLink(String href, HTMLDocument doc,
2109 JEditorPane editor, int offset) {
2110 try {
2111 URL page =
2112 (URL)doc.getProperty(Document.StreamDescriptionProperty);
2113 URL url = new URL(page, href);
2114 HyperlinkEvent linkEvent = new HyperlinkEvent
2115 (editor, HyperlinkEvent.EventType.
2249 }
2250 }
2251 }
2252
2253 private static int getBodyElementStart(JTextComponent comp) {
2254 Element rootElement = comp.getDocument().getRootElements()[0];
2255 for (int i = 0; i < rootElement.getElementCount(); i++) {
2256 Element currElement = rootElement.getElement(i);
2257 if("body".equals(currElement.getName())) {
2258 return currElement.getStartOffset();
2259 }
2260 }
2261 return 0;
2262 }
2263
2264 /*
2265 * Move the caret to the beginning of the document.
2266 * @see DefaultEditorKit#beginAction
2267 * @see HTMLEditorKit#getActions
2268 */
2269 @SuppressWarnings("serial") // Superclass is not serializable across versions
2270 static class BeginAction extends TextAction {
2271
2272 /* Create this object with the appropriate identifier. */
2273 BeginAction(String nm, boolean select) {
2274 super(nm);
2275 this.select = select;
2276 }
2277
2278 /** The operation to perform when this action is triggered. */
2279 public void actionPerformed(ActionEvent e) {
2280 JTextComponent target = getTextComponent(e);
2281 int bodyStart = getBodyElementStart(target);
2282
2283 if (target != null) {
2284 if (select) {
2285 target.moveCaretPosition(bodyStart);
2286 } else {
2287 target.setCaretPosition(bodyStart);
2288 }
2289 }
|