package net.atlanticbb.tantlinger.ui.text.actions; /* * Created on Dec 10, 2005 * */ import java.awt.event.ActionEvent; import java.io.StringWriter; import java.util.Enumeration; import javax.swing.Action; import javax.swing.JEditorPane; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Element; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import net.atlanticbb.tantlinger.ui.text.CompoundUndoManager; import net.atlanticbb.tantlinger.ui.text.ElementWriter; import net.atlanticbb.tantlinger.ui.text.HTMLUtils; /** * Action which properly inserts breaks for an HTMLDocument * * @author Bob Tantlinger * */ public class EnterKeyAction extends DecoratedTextAction { /** * */ private static final long serialVersionUID = 1L; //private Action delegate = null; /** * Creates a new EnterKeyAction. * @param defaultEnterAction Should be the default action */ public EnterKeyAction(Action defaultEnterAction) { super("EnterAction", defaultEnterAction); //delegate = defaultEnterAction; } public void actionPerformed(ActionEvent e) { JEditorPane editor; HTMLDocument document; try { editor = (JEditorPane)getTextComponent(e); document = (HTMLDocument)editor.getDocument(); } catch(ClassCastException ex) { // don't know what to do with this type // so pass off the event to the delegate delegate.actionPerformed(e); return; } Element elem = document.getParagraphElement(editor.getCaretPosition()); Element parentElem = elem.getParentElement(); HTML.Tag tag = HTML.getTag(elem.getName()); HTML.Tag parentTag = HTML.getTag(parentElem.getName()); int caret = editor.getCaretPosition(); CompoundUndoManager.beginCompoundEdit(document); try { if(HTMLUtils.isImplied(elem)) { //are we inside a list item? if(parentTag.equals(HTML.Tag.LI)) { //does the list item have any contents if(parentElem.getEndOffset() - parentElem.getStartOffset() > 1) { String txt = ""; //caret at start of listitem if(caret == parentElem.getStartOffset()) { document.insertBeforeStart(parentElem, toListItem(txt)); }//caret in the middle of list item content else if(caret < parentElem.getEndOffset() - 1 && caret > parentElem.getStartOffset()) { int len = parentElem.getEndOffset() - caret; txt = document.getText(caret, len); caret--;// hmmm document.insertAfterEnd(parentElem, toListItem(txt)); document.remove(caret, len); } else//caret at end of list item { document.insertAfterEnd(parentElem, toListItem(txt)); } editor.setCaretPosition(caret + 1); } else// empty list item { Element listParentElem = HTMLUtils.getListParent(parentElem).getParentElement(); //System.out.println(listParentElem.getName()); if(isListItem(HTML.getTag(listParentElem.getName())))//nested list { //System.out.println("nested list============"); //document.insertAfterEnd(parentElem, (toListItem(""))); //editor.setCaretPosition(elem.getEndOffset()); HTML.Tag listParentTag = HTML.getTag(HTMLUtils.getListParent(listParentElem).toString()); /*HTMLEditorKit.InsertHTMLTextAction a = new HTMLEditorKit.InsertHTMLTextAction("insert", "", listParentTag, HTML.Tag.LI); a.actionPerformed(e);*/ int start = parentElem.getStartOffset(); Element nextElem = HTMLUtils.getNextElement(document, parentElem); int len = nextElem.getEndOffset() - start; String ml = HTMLUtils.getElementHTML(listParentElem, true); //System.out.println(ml); //System.out.println("------------------"); ml = ml.replaceFirst("\\<li\\>\\s*\\<\\/li\\>\\s*\\<\\/ul\\>", "</ul>"); ml = ml.replaceFirst("\\<ul\\>\\s*\\<\\/ul\\>", ""); //System.out.println(ml); document.setOuterHTML(listParentElem, ml); //document.remove(start, len); //HTMLUtils.removeElement(elem); }//are we directly under a table cell? else if(listParentElem.getName().equals("td")) { //reset the table cell contents nested in a <div> //we do this because otherwise the next table cell would //get deleted!! Perhaps this is a bug in swing's html implemenation? encloseInDIV(listParentElem, document); editor.setCaretPosition(caret + 1); } else //end the list { if(isInList(listParentElem)) { //System.out.println("======nested list============"); HTML.Tag listParentTag = HTML.getTag(HTMLUtils.getListParent(listParentElem).toString()); HTMLEditorKit.InsertHTMLTextAction a = new HTMLEditorKit.InsertHTMLTextAction("insert", "<li></li>", listParentTag, HTML.Tag.LI); a.actionPerformed(e); } else { HTML.Tag root = HTML.Tag.BODY; if(HTMLUtils.getParent(elem, HTML.Tag.TD) != null) root = HTML.Tag.TD; HTMLEditorKit.InsertHTMLTextAction a = new HTMLEditorKit.InsertHTMLTextAction("insert", "<p></p>", root, HTML.Tag.P); a.actionPerformed(e); } HTMLUtils.removeElement(parentElem); } } } else //not a list { //System.out.println("IMPLIED DEFAULT"); //System.out.println("elem: " + elem.getName()); //System.out.println("pelem: " + parentElem.getName()); if(parentTag.isPreformatted()) { insertImpliedBR(e); } else if(parentTag.equals(HTML.Tag.TD)) { encloseInDIV(parentElem, document); editor.setCaretPosition(caret + 1); } else if(parentTag.equals(HTML.Tag.BODY) || isInList(elem)) { //System.out.println("insertParagraphAfter elem"); insertParagraphAfter(elem, editor); } else { //System.out.println("***insertParagraphAfter parentElem"); insertParagraphAfter(parentElem, editor); } } } else //not implied { //we need to check for this here in case any straggling li's //or dd's exist if(isListItem(tag)) { if((elem.getEndOffset() - editor.getCaretPosition()) == 1) { //System.out.println("inserting \\n "); //caret at end of para editor.replaceSelection("\n "); editor.setCaretPosition(editor.getCaretPosition() - 1); } else { //System.out.println("NOT implied delegate"); delegate.actionPerformed(e); } } else { //System.out.println("not implied insertparaafter1 " + elem.getName()); insertParagraphAfter(elem, editor); } } } catch (Exception ex) { ex.printStackTrace(); } CompoundUndoManager.endCompoundEdit(document); } private boolean isListItem(HTML.Tag t) { return (t.equals(HTML.Tag.LI) || t.equals(HTML.Tag.DT) || t.equals(HTML.Tag.DD)); } private String toListItem(String txt) { return "<li>" + txt + "</li>"; } private boolean isInList(Element el) { return HTMLUtils.getListParent(el) != null; } private void insertImpliedBR(ActionEvent e) { HTMLEditorKit.InsertHTMLTextAction hta = new HTMLEditorKit.InsertHTMLTextAction("insertBR", "<br>", HTML.Tag.IMPLIED, HTML.Tag.BR); hta.actionPerformed(e); } private void encloseInDIV(Element elem, HTMLDocument document) throws Exception { //System.out.println("enclosing in div: " + elem.getName()); HTML.Tag tag = HTML.getTag(elem.getName()); String html = HTMLUtils.getElementHTML(elem, false); html = HTMLUtils.createTag(tag, elem.getAttributes(), "<div>" + html + "</div><div></div>"); document.setOuterHTML(elem, html); } /** * Inserts a paragraph after the current paragraph of the same type * * @param elem * @param editor * @throws BadLocationException * @throws java.io.IOException */ private void insertParagraphAfter(Element elem, JEditorPane editor) throws BadLocationException, java.io.IOException { int cr = editor.getCaretPosition(); HTMLDocument document = (HTMLDocument)elem.getDocument(); HTML.Tag t = HTML.getTag(elem.getName()); int endOffs = elem.getEndOffset(); int startOffs = elem.getStartOffset(); //if this is an implied para, make the new para a div if(t == null || elem.getName().equals("p-implied")) t = HTML.Tag.DIV; String html; //got to test for this here, otherwise <hr> and <br> //get duplicated if(cr == startOffs) { html = createBlock(t, elem, ""); } else //split the current para at the cursor position { StringWriter out = new StringWriter(); ElementWriter w = new ElementWriter(out, elem, startOffs, cr); w.write(); html = createBlock(t, elem, out.toString()); } if(cr == endOffs - 1) { html += createBlock(t, elem, ""); } else { StringWriter out = new StringWriter(); ElementWriter w = new ElementWriter(out, elem, cr, endOffs); w.write(); html += createBlock(t, elem, out.toString()); } //copy the current para's character attributes AttributeSet chAttribs; if(endOffs > startOffs && cr == endOffs - 1) chAttribs = new SimpleAttributeSet(document.getCharacterElement(cr - 1).getAttributes()); else chAttribs = new SimpleAttributeSet(document.getCharacterElement(cr).getAttributes()); document.setOuterHTML(elem, html); cr++; Element p = document.getParagraphElement(cr); if(cr == endOffs) { //update the character attributes for the added paragraph //FIXME If the added paragraph is at the start/end //of the document, the char attrs dont get set setCharAttribs(p, chAttribs); } editor.setCaretPosition(p.getStartOffset()); } private String createBlock(HTML.Tag t, Element elem, String html) { AttributeSet attribs = elem.getAttributes(); return HTMLUtils.createTag(t, attribs, HTMLUtils.removeEnclosingTags(elem, html)); } private void setCharAttribs(Element p, AttributeSet chAttribs) { HTMLDocument document = (HTMLDocument)p.getDocument(); int start = p.getStartOffset(); int end = p.getEndOffset(); SimpleAttributeSet sas = new SimpleAttributeSet(chAttribs); sas.removeAttribute(HTML.Attribute.SRC); //if the charattribs contains a br, hr, or img attribute, it'll erase //any content in the paragraph boolean skipAttribs = false; for(Enumeration ee = sas.getAttributeNames(); ee.hasMoreElements();) { Object n = ee.nextElement(); String val = chAttribs.getAttribute(n).toString(); ////System.out.println(n + " " + val); skipAttribs = val.equals("br") || val.equals("hr") || val.equals("img"); } if(!skipAttribs) document.setCharacterAttributes(start, end - start, sas, true); } }