1 /* HTMLEditorKit.java --
2 Copyright (C) 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.text.html;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.MouseAdapter;
44 import java.awt.event.MouseEvent;
45 import java.awt.event.MouseMotionListener;
46 import java.awt.Cursor;
47 import java.awt.Point;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.InputStreamReader;
52 import java.io.Reader;
53 import java.io.Serializable;
54 import java.io.StringReader;
55 import java.io.Writer;
56 import java.net.MalformedURLException;
59 import javax.accessibility.Accessible;
60 import javax.accessibility.AccessibleContext;
62 import javax.swing.Action;
63 import javax.swing.JEditorPane;
64 import javax.swing.SwingUtilities;
65 import javax.swing.event.HyperlinkEvent;
66 import javax.swing.text.AttributeSet;
67 import javax.swing.text.BadLocationException;
68 import javax.swing.text.Document;
69 import javax.swing.text.EditorKit;
70 import javax.swing.text.Element;
71 import javax.swing.text.MutableAttributeSet;
72 import javax.swing.text.StyleConstants;
73 import javax.swing.text.StyledDocument;
74 import javax.swing.text.StyledEditorKit;
75 import javax.swing.text.TextAction;
76 import javax.swing.text.View;
77 import javax.swing.text.ViewFactory;
78 import javax.swing.text.html.parser.ParserDelegator;
80 /* Move these imports here after javax.swing.text.html to make it compile
82 import gnu.javax.swing.text.html.parser.GnuParserDelegator;
83 import gnu.javax.swing.text.html.parser.HTML_401F;
86 * @author Lillian Angel (langel at redhat dot com)
88 public class HTMLEditorKit
89 extends StyledEditorKit
90 implements Serializable, Cloneable, Accessible
94 * Fires the hyperlink events on the associated component
97 public static class LinkController
99 implements MouseMotionListener, Serializable
103 * The element of the last anchor tag.
105 private Element lastAnchorElement;
110 public LinkController()
116 * Dispatched when the mouse is clicked. If the component
117 * is read-only, then the clicked event is used to drive an
118 * attempt to follow the reference specified by a link
120 * @param e - the mouse event
122 public void mouseClicked(MouseEvent e)
124 JEditorPane editor = (JEditorPane) e.getSource();
125 if (! editor.isEditable() && SwingUtilities.isLeftMouseButton(e))
127 Point loc = e.getPoint();
128 int pos = editor.viewToModel(loc);
130 activateLink(pos, editor, e.getX(), e.getY());
135 * Dispatched when the mouse is dragged on a component.
137 * @param e - the mouse event.
139 public void mouseDragged(MouseEvent e)
141 // Nothing to do here.
145 * Dispatched when the mouse cursor has moved into the component.
147 * @param e - the mouse event.
149 public void mouseMoved(MouseEvent e)
151 JEditorPane editor = (JEditorPane) e.getSource();
152 HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
153 if (! editor.isEditable())
155 Document doc = editor.getDocument();
156 if (doc instanceof HTMLDocument)
158 Cursor newCursor = kit.getDefaultCursor();
159 HTMLDocument htmlDoc = (HTMLDocument) doc;
160 Point loc = e.getPoint();
161 int pos = editor.viewToModel(loc);
162 Element el = htmlDoc.getCharacterElement(pos);
163 if (pos < el.getStartOffset() || pos >= el.getEndOffset())
167 AttributeSet aAtts = (AttributeSet)
168 el.getAttributes().getAttribute(HTML.Tag.A);
171 if (el != lastAnchorElement)
173 if (lastAnchorElement != null)
174 htmlDoc.updateSpecialClass(lastAnchorElement,
175 HTML.Attribute.DYNAMIC_CLASS,
177 lastAnchorElement = el;
178 htmlDoc.updateSpecialClass(el,
179 HTML.Attribute.DYNAMIC_CLASS,
182 newCursor = kit.getLinkCursor();
186 if (lastAnchorElement != null)
187 htmlDoc.updateSpecialClass(lastAnchorElement,
188 HTML.Attribute.DYNAMIC_CLASS,
190 lastAnchorElement = null;
195 if (lastAnchorElement != null)
196 htmlDoc.updateSpecialClass(lastAnchorElement,
197 HTML.Attribute.DYNAMIC_CLASS,
199 lastAnchorElement = null;
201 if (editor.getCursor() != newCursor)
203 editor.setCursor(newCursor);
210 * If the given position represents a link, then linkActivated is called
211 * on the JEditorPane.
213 * @param pos the position
214 * @param editor the editor pane
216 protected void activateLink(int pos, JEditorPane editor)
218 activateLink(pos, editor);
221 private void activateLink(int pos, JEditorPane editor, int x, int y)
223 // TODO: This is here for future extension for mapped links support.
224 // For the time beeing we implement simple hyperlinks.
225 Document doc = editor.getDocument();
226 if (doc instanceof HTMLDocument)
228 HTMLDocument htmlDoc = (HTMLDocument) doc;
229 Element el = htmlDoc.getCharacterElement(pos);
230 AttributeSet atts = el.getAttributes();
231 AttributeSet anchorAtts =
232 (AttributeSet) atts.getAttribute(HTML.Tag.A);
234 if (anchorAtts != null)
236 href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF);
237 htmlDoc.updateSpecialClass(el, HTML.Attribute.PSEUDO_CLASS,
242 // TODO: Implement link maps here.
244 HyperlinkEvent event = null;
246 event = createHyperlinkEvent(editor, htmlDoc, href,
249 editor.fireHyperlinkUpdate(event);
255 * Creates a HyperlinkEvent for the specified href and anchor if
256 * possible. If for some reason this won't work, return null.
258 * @param editor the editor
259 * @param doc the document
260 * @param href the href link
261 * @param anchor the anchor
262 * @param el the element
264 * @return the hyperlink event, or <code>null</code> if we couldn't
267 private HyperlinkEvent createHyperlinkEvent(JEditorPane editor,
276 URL base = doc.getBase();
277 url = new URL(base, href);
280 catch (MalformedURLException ex)
285 if (doc.isFrameDocument())
287 String target = null;
289 target = (String) anchor.getAttribute(HTML.Attribute.TARGET);
290 if (target == null || target.equals(""))
291 target = doc.getBaseTarget();
292 if (target == null || target.equals(""))
294 ev = new HTMLFrameHyperlinkEvent(editor,
295 HyperlinkEvent.EventType.ACTIVATED,
296 url, href, el, target);
300 ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED,
308 * This class is used to insert a string of HTML into an existing
309 * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag)
310 * identifies the parent in the document to add the elements to. The second, (addTag),
311 * identifies that the first tag should be added to the document as seen in the string.
312 * The parser will generate all appropriate (opening/closing tags_ even if they are not
313 * in the HTML string passed in.
315 public static class InsertHTMLTextAction
316 extends HTMLTextAction
320 * Tag in HTML to start adding tags from.
322 protected HTML.Tag addTag;
325 * Alternate tag in HTML to start adding tags from if parentTag is
326 * not found and alternateParentTag is not found.
328 protected HTML.Tag alternateAddTag;
331 * Alternate tag to check if parentTag is not found.
333 protected HTML.Tag alternateParentTag;
338 protected String html;
341 * Tag to check for in the document.
343 protected HTML.Tag parentTag;
346 * Initializes all fields.
348 * @param name - the name of the document.
349 * @param html - the html to insert
350 * @param parentTag - the parent tag to check for
351 * @param addTag - the tag to start adding from
353 public InsertHTMLTextAction(String name, String html,
354 HTML.Tag parentTag, HTML.Tag addTag)
356 this(name, html, parentTag, addTag, null, null);
360 * Initializes all fields and calls super
362 * @param name - the name of the document.
363 * @param html - the html to insert
364 * @param parentTag - the parent tag to check for
365 * @param addTag - the tag to start adding from
366 * @param alternateParentTag - the alternate parent tag
367 * @param alternateAddTag - the alternate add tag
369 public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag,
370 HTML.Tag addTag, HTML.Tag alternateParentTag,
371 HTML.Tag alternateAddTag)
374 // Fields are for easy access when the action is applied to an actual
377 this.parentTag = parentTag;
378 this.addTag = addTag;
379 this.alternateParentTag = alternateParentTag;
380 this.alternateAddTag = alternateAddTag;
384 * HTMLEditorKit.insertHTML is called. If an exception is
385 * thrown, it is wrapped in a RuntimeException and thrown.
387 * @param editor - the editor to use to get the editorkit
389 * the Document to insert the HTML into.
391 * where to begin inserting the HTML.
393 * the String to insert
395 * the number of ElementSpec.EndTagTypes to generate before
398 * the number of ElementSpec.StartTagTypes with a direction of
399 * ElementSpec.JoinNextDirection that should be generated before
401 * the first tag to start inserting into document
403 protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset,
404 String html, int popDepth, int pushDepth,
409 super.getHTMLEditorKit(editor).insertHTML(doc, offset, html,
410 popDepth, pushDepth, addTag);
412 catch (IOException e)
414 throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e);
416 catch (BadLocationException ex)
418 throw (RuntimeException) new RuntimeException("BadLocationException: "
419 + offset).initCause(ex);
424 * Invoked when inserting at a boundary. Determines the number of pops,
425 * and then the number of pushes that need to be performed. The it calls
429 * the editor to use to get the editorkit
431 * the Document to insert the HTML into.
433 * where to begin inserting the HTML.
434 * @param insertElement -
435 * the element to insert
443 protected void insertAtBoundary(JEditorPane editor,
444 HTMLDocument doc, int offset,
445 Element insertElement,
446 String html, HTML.Tag parentTag,
449 insertAtBoundry(editor, doc, offset, insertElement,
450 html, parentTag, addTag);
454 * Invoked when inserting at a boundary. Determines the number of pops,
455 * and then the number of pushes that need to be performed. The it calls
458 * @param editor - the editor to use to get the editorkit
460 * the Document to insert the HTML into.
462 * where to begin inserting the HTML.
463 * @param insertElement - the element to insert
464 * @param html - the html to insert
465 * @param parentTag - the parent tag
466 * @param addTag - the first tag
468 * @deprecated as of v1.3, use insertAtBoundary
470 protected void insertAtBoundry(JEditorPane editor,
472 int offset, Element insertElement,
473 String html, HTML.Tag parentTag,
476 Element parent = insertElement;
478 // Find common parent element.
479 if (offset > 0 || insertElement == null)
481 el = doc.getDefaultRootElement();
482 while (el != null && el.getStartOffset() != offset
484 el = el.getElement(el.getElementIndex(offset));
485 parent = el != null ? el.getParentElement() : null;
491 if (offset == 0 && insertElement != null)
494 while (el != null && ! el.isLeaf())
496 el = el.getElement(el.getElementIndex(offset));
504 while (el != null && ! el.isLeaf())
506 el = el.getElement(el.getElementIndex(offset));
511 while (el != null && el != insertElement)
513 el = el.getElement(el.getElementIndex(offset));
517 pops = Math.max(0, pops - 1);
518 insertHTML(editor, doc, offset, html, pops, pushes, addTag);
525 * @param ae - the action performed
527 public void actionPerformed(ActionEvent ae)
529 JEditorPane source = getEditor(ae);
532 HTMLDocument d = getHTMLDocument(source);
533 int offset = source.getSelectionStart();
534 int length = d.getLength();
535 boolean inserted = true;
536 if (! tryInsert(source, d, offset, parentTag, addTag))
538 inserted = tryInsert(source, d, offset, alternateParentTag,
542 adjustSelection(source, d, offset, length);
547 * Tries to insert the html chunk to the specified <code>addTag</code>.
549 * @param pane the editor
550 * @param doc the document
551 * @param offset the offset at which to insert
552 * @param tag the tag at which to insert
553 * @param addTag the add tag
555 * @return <code>true</code> when the html has been inserted successfully,
556 * <code>false</code> otherwise
558 private boolean tryInsert(JEditorPane pane, HTMLDocument doc, int offset,
559 HTML.Tag tag, HTML.Tag addTag)
561 boolean inserted = false;
562 Element el = findElementMatchingTag(doc, offset, tag);
563 if (el != null && el.getStartOffset() == offset)
565 insertAtBoundary(pane, doc, offset, el, html, tag, addTag);
570 int depth = elementCountToTag(doc, offset - 1, tag);
573 insertHTML(pane, doc, offset, html, depth, 0, addTag);
581 * Adjusts the selection after an insertion has been performed.
583 * @param pane the editor pane
584 * @param doc the document
585 * @param offset the insert offset
586 * @param oldLen the old document length
588 private void adjustSelection(JEditorPane pane, HTMLDocument doc,
589 int offset, int oldLen)
591 int newLen = doc.getLength();
592 if (newLen != oldLen && offset < newLen)
599 text = doc.getText(offset - 1, 1);
601 catch (BadLocationException ex)
605 if (text != null && text.length() > 0
606 && text.charAt(0) == '\n')
608 pane.select(offset, offset);
612 pane.select(offset + 1, offset + 1);
624 * Abstract Action class that helps inserting HTML into an existing document.
626 public abstract static class HTMLTextAction
627 extends StyledEditorKit.StyledTextAction
633 public HTMLTextAction(String name)
639 * Gets the HTMLDocument from the JEditorPane.
641 * @param e - the editor pane
642 * @return the html document.
644 protected HTMLDocument getHTMLDocument(JEditorPane e)
646 Document d = e.getDocument();
647 if (d instanceof HTMLDocument)
648 return (HTMLDocument) d;
649 throw new IllegalArgumentException("Document is not a HTMLDocument.");
653 * Gets the HTMLEditorKit
655 * @param e - the JEditorPane to get the HTMLEditorKit from.
656 * @return the HTMLEditorKit
658 protected HTMLEditorKit getHTMLEditorKit(JEditorPane e)
660 EditorKit d = e.getEditorKit();
661 if (d instanceof HTMLEditorKit)
662 return (HTMLEditorKit) d;
663 throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit.");
667 * Returns an array of Elements that contain the offset.
668 * The first elements corresponds to the roots of the doc.
670 * @param doc - the document to get the Elements from.
671 * @param offset - the offset the Elements must contain
672 * @return an array of all the elements containing the offset.
674 protected Element[] getElementsAt(HTMLDocument doc,
677 return getElementsAt(doc.getDefaultRootElement(), offset, 0);
681 * Helper function to get all elements using recursion.
683 private Element[] getElementsAt(Element root, int offset, int depth)
685 Element[] elements = null;
690 elements = new Element[depth + 1];
691 elements[depth] = root;
694 elements = getElementsAt(root.getElement(root.getElementIndex(offset)),
696 elements[depth] = root;
702 * Returns the number of elements, starting at the deepest point, needed
703 * to get an element representing tag. -1 if no elements are found, 0 if
704 * the parent of the leaf at offset represents the tag.
707 * the document to search
709 * the offset to check
711 * the tag to look for
712 * @return - the number of elements needed to get an element representing
715 protected int elementCountToTag(HTMLDocument doc,
716 int offset, HTML.Tag tag)
718 Element root = doc.getDefaultRootElement();
720 Element next = root.getElement(root.getElementIndex(offset));
722 while (!next.isLeaf())
725 if (next.getAttributes().
726 getAttribute(StyleConstants.NameAttribute).equals(tag))
728 next = next.getElement(next.getElementIndex(offset));
734 * Gets the deepest element at offset with the
737 * @param doc - the document to search
738 * @param offset - the offset to check for
739 * @param tag - the tag to match
740 * @return - the element that is found, null if not found.
742 protected Element findElementMatchingTag(HTMLDocument doc,
743 int offset, HTML.Tag tag)
745 Element element = doc.getDefaultRootElement();
746 Element tagElement = null;
748 while (element != null)
750 Object otag = element.getAttributes().getAttribute(
751 StyleConstants.NameAttribute);
752 if (otag instanceof HTML.Tag && otag.equals(tag))
753 tagElement = element;
754 element = element.getElement(element.getElementIndex(offset));
762 * A {@link ViewFactory} that is able to create {@link View}s for
763 * the <code>Element</code>s that are supported.
765 public static class HTMLFactory
766 implements ViewFactory
778 * Creates a {@link View} for the specified <code>Element</code>.
780 * @param element the <code>Element</code> to create a <code>View</code>
782 * @return the <code>View</code> for the specified <code>Element</code>
783 * or <code>null</code> if the type of <code>element</code> is
786 public View create(Element element)
790 element.getAttributes().getAttribute(StyleConstants.NameAttribute);
791 if (attr instanceof HTML.Tag)
793 HTML.Tag tag = (HTML.Tag) attr;
795 if (tag == HTML.Tag.IMPLIED || tag == HTML.Tag.P
796 || tag == HTML.Tag.H1 || tag == HTML.Tag.H2
797 || tag == HTML.Tag.H3 || tag == HTML.Tag.H4
798 || tag == HTML.Tag.H5 || tag == HTML.Tag.H6
799 || tag == HTML.Tag.DT)
800 view = new ParagraphView(element);
801 else if (tag == HTML.Tag.LI || tag == HTML.Tag.DL
802 || tag == HTML.Tag.DD || tag == HTML.Tag.BODY
803 || tag == HTML.Tag.HTML || tag == HTML.Tag.CENTER
804 || tag == HTML.Tag.DIV
805 || tag == HTML.Tag.BLOCKQUOTE
806 || tag == HTML.Tag.PRE
807 || tag == HTML.Tag.FORM
808 // Misplaced TD and TH tags get mapped as vertical block.
809 // Note that correctly placed tags get mapped in TableView.
810 || tag == HTML.Tag.TD || tag == HTML.Tag.TH)
811 view = new BlockView(element, View.Y_AXIS);
812 else if (tag == HTML.Tag.TR)
813 // Misplaced TR tags get mapped as horizontal blocks.
814 // Note that correctly placed tags get mapped in TableView.
815 view = new BlockView(element, View.X_AXIS);
816 else if (tag == HTML.Tag.IMG)
817 view = new ImageView(element);
819 else if (tag == HTML.Tag.CONTENT)
820 view = new InlineView(element);
821 else if (tag == HTML.Tag.HEAD)
822 view = new NullView(element);
823 else if (tag == HTML.Tag.TABLE)
824 view = new javax.swing.text.html.TableView(element);
825 else if (tag == HTML.Tag.HR)
826 view = new HRuleView(element);
827 else if (tag == HTML.Tag.BR)
828 view = new BRView(element);
829 else if (tag == HTML.Tag.INPUT || tag == HTML.Tag.SELECT
830 || tag == HTML.Tag.TEXTAREA)
831 view = new FormView(element);
833 else if (tag == HTML.Tag.MENU || tag == HTML.Tag.DIR
834 || tag == HTML.Tag.UL || tag == HTML.Tag.OL)
835 view = new ListView(element);
836 else if (tag == HTML.Tag.FRAMESET)
837 view = new FrameSetView(element);
838 else if (tag == HTML.Tag.FRAME)
839 view = new FrameView(element);
840 else if (tag == HTML.Tag.OBJECT)
841 view = new ObjectView(element);
845 view = new NullView(element);
852 * The abstract HTML parser declaration.
854 public abstract static class Parser
857 * Parse the HTML text, calling various methods of the provided callback
858 * in response to the occurence of the corresponding HTML constructions.
859 * @param reader The reader to read the source HTML from.
860 * @param callback The callback to receive information about the parsed
862 * @param ignoreCharSet If true, the parser ignores all charset information
863 * that may be present in HTML documents.
864 * @throws IOException, normally if the reader throws one.
866 public abstract void parse(Reader reader, ParserCallback callback,
867 boolean ignoreCharSet) throws IOException;
871 * The "hook" that receives all information about the HTML document
872 * structure while parsing it. The methods are invoked by parser
873 * and should be normally overridden.
875 public static class ParserCallback
878 * If the tag does not occurs in the html stream directly, but
879 * is supposed by parser, the tag attribute set contains this additional
880 * attribute, having value Boolean.True.
882 public static final Object IMPLIED = "_implied_";
887 public ParserCallback()
889 // Nothing to do here.
893 * The parser calls this method after it finishes parsing the document.
895 public void flush() throws BadLocationException
897 // Nothing to do here.
901 * Handle HTML comment, present in the given position.
902 * @param comment the comment
903 * @position the position of the comment in the text being parsed.
905 public void handleComment(char[] comment, int position)
907 // Nothing to do here.
911 * Notifies about the character sequences, used to separate lines in
912 * this document. The parser calls this method after it finishes
913 * parsing the document, but before flush().
914 * @param end_of_line The "end of line sequence", one of: \r or \n or \r\n.
916 public void handleEndOfLineString(String end_of_line)
918 // Nothing to do here.
922 * The method is called when the HTML closing tag ((like </table>)
923 * is found or if the parser concludes that the one should be present
924 * in the current position.
925 * @param tag The tag being handled
926 * @param position the tag position in the text being parsed.
928 public void handleEndTag(HTML.Tag tag, int position)
930 // Nothing to do here.
935 * @param message The message, explaining the error.
936 * @param position The starting position of the fragment that has caused
937 * the error in the html document being parsed.
939 public void handleError(String message, int position)
941 // Nothing to do here.
945 * Handle the tag with no content, like <br>. The method is
946 * called for the elements that, in accordance with the current DTD,
947 * has an empty content.
948 * @param tag The tag being handled.
949 * @param position The tag position in the text being parsed.
951 public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes,
954 // Nothing to do here.
958 * The method is called when the HTML opening tag ((like <table>)
959 * is found or if the parser concludes that the one should be present
960 * in the current position.
961 * @param tag The tag being handled
962 * @param position The tag position in the text being parsed
964 public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes,
967 // Nothing to do here.
971 * Handle the text section.
972 * @param text A section text.
973 * @param position The text position in the HTML document text being parsed.
975 public void handleText(char[] text, int position)
977 // Nothing to do here.
982 * Use serialVersionUID (v1.4) for interoperability.
984 private static final long serialVersionUID = 8751997116710384592L;
987 * Default cascading stylesheed file ("default.css").
989 public static final String DEFAULT_CSS = "default.css";
992 * The <b>bold</b> action identifier.
994 public static final String BOLD_ACTION = "html-bold-action";
997 * The <i>italic</i> action identifier.
999 public static final String ITALIC_ACTION = "html-italic-action";
1002 * The <font color="#FF0000">color</font> action indentifier
1003 * (passing the color as an argument).
1005 public static final String COLOR_ACTION = "html-color-action";
1008 * The <font size="+1">increase</font> font action identifier.
1010 public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
1013 * The <font size="-1">decrease</font> font action identifier.
1015 public static final String FONT_CHANGE_SMALLER = "html-font-smaller";
1018 * Align images at the bottom.
1020 public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom";
1023 * Align images at the middle.
1025 public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle";
1028 * Align images at the top.
1030 public static final String IMG_ALIGN_TOP = "html-image-align-top";
1033 * Align images at the border.
1035 public static final String IMG_BORDER = "html-image-border";
1038 * The "logical style" action identifier, passing that style as parameter.
1040 public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action";
1043 * The "ident paragraph left" action.
1045 public static final String PARA_INDENT_LEFT = "html-para-indent-left";
1048 * The "ident paragraph right" action.
1050 public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
1055 private static final Action[] defaultActions =
1057 new InsertHTMLTextAction("InsertTable",
1058 "<table border=1><tr><td></td></tr></table>",
1059 HTML.Tag.BODY, HTML.Tag.TABLE),
1060 new InsertHTMLTextAction("InsertTableRow",
1061 "<table border=1><tr><td></td></tr></table>",
1062 HTML.Tag.TABLE, HTML.Tag.TR,
1063 HTML.Tag.BODY, HTML.Tag.TABLE),
1064 new InsertHTMLTextAction("InsertTableCell",
1065 "<table border=1><tr><td></td></tr></table>",
1066 HTML.Tag.TR, HTML.Tag.TD,
1067 HTML.Tag.BODY, HTML.Tag.TABLE),
1068 new InsertHTMLTextAction("InsertUnorderedList",
1069 "<ul><li></li></ul>",
1070 HTML.Tag.BODY, HTML.Tag.UL),
1071 new InsertHTMLTextAction("InsertUnorderedListItem",
1072 "<ul><li></li></ul>",
1073 HTML.Tag.UL, HTML.Tag.LI,
1074 HTML.Tag.BODY, HTML.Tag.UL),
1075 new InsertHTMLTextAction("InsertOrderedList",
1076 "<ol><li></li></ol>",
1077 HTML.Tag.BODY, HTML.Tag.OL),
1078 new InsertHTMLTextAction("InsertOrderedListItem",
1079 "<ol><li></li></ol>",
1080 HTML.Tag.OL, HTML.Tag.LI,
1081 HTML.Tag.BODY, HTML.Tag.OL),
1082 new InsertHTMLTextAction("InsertPre",
1083 "<pre></pre>", HTML.Tag.BODY, HTML.Tag.PRE)
1084 // TODO: The reference impl has an InsertHRAction too.
1088 * The current style sheet.
1090 private StyleSheet styleSheet;
1093 * The ViewFactory for HTMLFactory.
1095 HTMLFactory viewFactory;
1098 * The Cursor for links.
1103 * The default cursor.
1105 Cursor defaultCursor;
1113 * The mouse listener used for links.
1115 private LinkController linkController;
1117 /** The content type */
1118 String contentType = "text/html";
1120 /** The input attributes defined by default.css */
1121 MutableAttributeSet inputAttributes;
1123 /** The editor pane used. */
1124 JEditorPane editorPane;
1127 * Whether or not the editor kit handles form submissions.
1129 * @see #isAutoFormSubmission()
1130 * @see #setAutoFormSubmission(boolean)
1132 private boolean autoFormSubmission;
1135 * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
1137 public HTMLEditorKit()
1139 linkController = new LinkController();
1140 autoFormSubmission = true;
1144 * Gets a factory suitable for producing views of any
1145 * models that are produced by this kit.
1147 * @return the view factory suitable for producing views.
1149 public ViewFactory getViewFactory()
1151 if (viewFactory == null)
1152 viewFactory = new HTMLFactory();
1157 * Create a text storage model for this type of editor.
1161 public Document createDefaultDocument()
1163 // Protect the shared stylesheet.
1164 StyleSheet styleSheet = getStyleSheet();
1165 StyleSheet ss = new StyleSheet();
1166 ss.addStyleSheet(styleSheet);
1168 HTMLDocument document = new HTMLDocument(ss);
1169 document.setParser(getParser());
1170 document.setAsynchronousLoadPriority(4);
1171 document.setTokenThreshold(100);
1176 * Get the parser that this editor kit uses for reading HTML streams. This
1177 * method can be overridden to use the alternative parser.
1179 * @return the HTML parser (by default, {@link ParserDelegator}).
1181 protected Parser getParser()
1185 parser = new GnuParserDelegator(HTML_401F.getInstance());
1191 * Inserts HTML into an existing document.
1193 * @param doc - the Document to insert the HTML into.
1194 * @param offset - where to begin inserting the HTML.
1195 * @param html - the String to insert
1196 * @param popDepth - the number of ElementSpec.EndTagTypes
1197 * to generate before inserting
1198 * @param pushDepth - the number of ElementSpec.StartTagTypes
1199 * with a direction of ElementSpec.JoinNextDirection that
1200 * should be generated before
1201 * @param insertTag - the first tag to start inserting into document
1202 * @throws IOException - on any I/O error
1203 * @throws BadLocationException - if pos represents an invalid location
1204 * within the document
1206 public void insertHTML(HTMLDocument doc, int offset, String html,
1207 int popDepth, int pushDepth, HTML.Tag insertTag)
1208 throws BadLocationException, IOException
1210 Parser parser = getParser();
1211 if (offset < 0 || offset > doc.getLength())
1212 throw new BadLocationException("Bad location", offset);
1214 throw new IOException("Parser is null.");
1216 ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag);
1218 // FIXME: What should ignoreCharSet be set to?
1220 // parser.parse inserts html into the buffer
1221 parser.parse(new StringReader(html), pc, false);
1226 * Inserts content from the given stream. Inserting HTML into a non-empty
1227 * document must be inside the body Element, if you do not insert into
1228 * the body an exception will be thrown. When inserting into a non-empty
1229 * document all tags outside of the body (head, title) will be dropped.
1231 * @param in - the stream to read from
1232 * @param doc - the destination for the insertion
1233 * @param pos - the location in the document to place the content
1234 * @throws IOException - on any I/O error
1235 * @throws BadLocationException - if pos represents an invalid location
1236 * within the document
1238 public void read(Reader in, Document doc, int pos) throws IOException,
1239 BadLocationException
1241 if (doc instanceof HTMLDocument)
1243 Parser parser = getParser();
1244 if (pos < 0 || pos > doc.getLength())
1245 throw new BadLocationException("Bad location", pos);
1247 throw new IOException("Parser is null.");
1249 HTMLDocument hd = ((HTMLDocument) doc);
1250 if (editorPane != null)
1251 hd.setBase(editorPane.getPage());
1252 ParserCallback pc = hd.getReader(pos);
1254 // FIXME: What should ignoreCharSet be set to?
1256 // parser.parse inserts html into the buffer
1257 parser.parse(in, pc, false);
1261 // read in DefaultEditorKit is called.
1262 // the string is inserted in the document as usual.
1263 super.read(in, doc, pos);
1267 * Writes content from a document to the given stream in
1268 * an appropriate format.
1270 * @param out - the stream to write to
1271 * @param doc - the source for the write
1272 * @param pos - the location in the document to get the content.
1273 * @param len - the amount to write out
1274 * @throws IOException - on any I/O error
1275 * @throws BadLocationException - if pos represents an invalid location
1276 * within the document
1278 public void write(Writer out, Document doc, int pos, int len)
1279 throws IOException, BadLocationException
1281 if (doc instanceof HTMLDocument)
1283 HTMLWriter writer = new HTMLWriter(out, (HTMLDocument) doc, pos, len);
1286 else if (doc instanceof StyledDocument)
1288 MinimalHTMLWriter writer = new MinimalHTMLWriter(out,
1289 (StyledDocument) doc,
1294 super.write(out, doc, pos, len);
1298 * Gets the content type that the kit supports.
1299 * This kit supports the type text/html.
1301 * @returns the content type supported.
1303 public String getContentType()
1309 * Creates a copy of the editor kit.
1311 * @return a copy of this.
1313 public Object clone()
1315 // FIXME: Need to clone all fields
1316 HTMLEditorKit copy = (HTMLEditorKit) super.clone();
1317 copy.linkController = new LinkController();
1322 * Copies the key/values in elements AttributeSet into set.
1323 * This does not copy component, icon, or element names attributes.
1324 * This is called anytime the caret moves over a different location.
1326 * @param element - the element to create the input attributes for.
1327 * @param set - the set to copy the values into.
1329 protected void createInputAttributes(Element element,
1330 MutableAttributeSet set)
1332 set.removeAttributes(set);
1333 set.addAttributes(element.getAttributes());
1334 // FIXME: Not fully implemented.
1338 * Called when this is installed into the JEditorPane.
1340 * @param c - the JEditorPane installed into.
1342 public void install(JEditorPane c)
1345 c.addMouseListener(linkController);
1346 c.addMouseMotionListener(linkController);
1351 * Called when the this is removed from the JEditorPane.
1352 * It unregisters any listeners.
1354 * @param c - the JEditorPane being removed from.
1356 public void deinstall(JEditorPane c)
1359 c.removeMouseListener(linkController);
1360 c.removeMouseMotionListener(linkController);
1365 * Gets the AccessibleContext associated with this.
1367 * @return the AccessibleContext for this.
1369 public AccessibleContext getAccessibleContext()
1371 // FIXME: Should return an instance of
1372 // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext
1373 // Not implemented yet.
1378 * Gets the action list. This list is supported by the superclass
1379 * augmented by the collection of actions defined locally for style
1382 * @return an array of all the actions
1384 public Action[] getActions()
1386 return TextAction.augmentList(super.getActions(), defaultActions);
1390 * Returns the default cursor.
1392 * @return the default cursor
1394 public Cursor getDefaultCursor()
1396 if (defaultCursor == null)
1397 defaultCursor = Cursor.getDefaultCursor();
1398 return defaultCursor;
1402 * Returns the cursor for links.
1404 * @return the cursor for links.
1406 public Cursor getLinkCursor()
1408 if (linkCursor == null)
1409 linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
1414 * Sets the Cursor for links.
1416 * @param cursor - the new cursor for links.
1418 public void setLinkCursor(Cursor cursor)
1420 linkCursor = cursor;
1424 * Sets the default cursor.
1426 * @param cursor - the new default cursor.
1428 public void setDefaultCursor(Cursor cursor)
1430 defaultCursor = cursor;
1434 * Gets the input attributes used for the styled editing actions.
1436 * @return the attribute set
1438 public MutableAttributeSet getInputAttributes()
1440 return inputAttributes;
1444 * Get the set of styles currently being used to render the HTML elements.
1445 * By default the resource specified by DEFAULT_CSS gets loaded, and is
1446 * shared by all HTMLEditorKit instances.
1448 * @return the style sheet.
1450 public StyleSheet getStyleSheet()
1452 if (styleSheet == null)
1456 styleSheet = new StyleSheet();
1457 Class c = HTMLEditorKit.class;
1458 InputStream in = c.getResourceAsStream(DEFAULT_CSS);
1459 InputStreamReader r = new InputStreamReader(in);
1460 styleSheet.loadRules(r, null);
1463 catch (IOException ex)
1465 // No style available.
1472 * Set the set of styles to be used to render the various HTML elements.
1473 * These styles are specified in terms of CSS specifications. Each document
1474 * produced by the kit will have a copy of the sheet which it can add the
1475 * document specific styles to. By default, the StyleSheet specified is shared
1476 * by all HTMLEditorKit instances.
1478 * @param s - the new style sheet
1480 public void setStyleSheet(StyleSheet s)
1486 * Returns <code>true</code> when forms should be automatically submitted
1487 * by the editor kit. Set this to <code>false</code> when you want to
1488 * intercept form submission. In this case you'd want to listen for
1489 * hyperlink events on the document and handle FormSubmitEvents specially.
1491 * The default is <code>true</code>.
1493 * @return <code>true</code> when forms should be automatically submitted
1494 * by the editor kit, <code>false</code> otherwise
1498 * @see #setAutoFormSubmission(boolean)
1499 * @see FormSubmitEvent
1501 public boolean isAutoFormSubmission()
1503 return autoFormSubmission;
1507 * Sets whether or not the editor kit should automatically submit forms.
1509 * @param auto <code>true</code> when the editor kit should handle form
1510 * submission, <code>false</code> otherwise
1514 * @see #isAutoFormSubmission()
1516 public void setAutoFormSubmission(boolean auto)
1518 autoFormSubmission = auto;