/* * Created on Nov 19, 2007 */ package net.atlanticbb.tantlinger.ui.text.actions; import java.awt.event.ActionEvent; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.JEditorPane; import javax.swing.text.BadLocationException; import javax.swing.text.Element; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; 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.HTMLUtils; /** * @author Bob Tantlinger * */ public class IndentAction extends HTMLTextEditAction { private static final long serialVersionUID = 1L; public static final int INDENT = 0; public static final int OUTDENT = 1; protected int direction; /** * @param name */ public IndentAction(int direction) throws IllegalArgumentException { super(""); if(direction == INDENT) putValue(NAME, "Indent"); else if(direction == OUTDENT) putValue(NAME, "Outdent"); else throw new IllegalArgumentException("Invalid indentation direction"); this.direction = direction; } /* (non-Javadoc) * @see net.atlanticbb.tantlinger.ui.text.actions.HTMLTextEditAction#sourceEditPerformed(java.awt.event.ActionEvent, javax.swing.JEditorPane) */ protected void sourceEditPerformed(ActionEvent e, JEditorPane editor) { // TODO Auto-generated method stub } private void insertHTML(String html, HTML.Tag tag, HTML.Tag root, ActionEvent e) { HTMLEditorKit.InsertHTMLTextAction a = new HTMLEditorKit.InsertHTMLTextAction("insertHTML", html, root, tag);//$NON-NLS-1$ a.actionPerformed(e); } private HTML.Tag getRootTag(Element elem) { HTML.Tag root = HTML.Tag.BODY; if(HTMLUtils.getListParent(elem) != null) { root = HTML.Tag.UL; } else if(HTMLUtils.getParent(elem, HTML.Tag.TD) != null) root = HTML.Tag.TD; else if(HTMLUtils.getParent(elem, HTML.Tag.BLOCKQUOTE) != null) root = HTML.Tag.BLOCKQUOTE; return root; } private int getIndentationLevel(Element el) { int level = 0; while((!el.getName().equals("body")) && (!el.getName().equals("td"))) { if(el.getName().equals("blockquote")) level++; el = el.getParentElement(); } return level; } private Map getListElems(List elems) { Map lis = new HashMap(); for(Iterator it = elems.iterator(); it.hasNext();) { Element li = HTMLUtils.getParent((Element)it.next(), HTML.Tag.LI); if(li != null) { Element listEl = HTMLUtils.getListParent(li); if(!lis.containsKey(listEl)) { lis.put(listEl, new ArrayList()); } List elList = (List)lis.get(listEl); elList.add(li); } } return lis; } private void unindent(ActionEvent e, JEditorPane editor) { List elems = getParagraphElements(editor); if(elems.size() == 0) return; List listElems = getLeadingTralingListElems(elems); elems.removeAll(listElems); Set elsToIndent = new HashSet(); Set elsToOutdent = new HashSet(); Element lastBqParent = null; for(int i = 0; i < elems.size(); i++) { Element el = (Element)elems.get(i); Element bqParent = HTMLUtils.getParent(el, HTML.Tag.BLOCKQUOTE); if(bqParent == null) continue; if(lastBqParent == null || bqParent.getStartOffset() >= lastBqParent.getEndOffset()) { elsToOutdent.add(bqParent); lastBqParent = bqParent; } if(i == 0 || i == elems.size() - 1) { int c = bqParent.getElementCount(); for(int j = 0; j < c; j++) { Element bqChild = bqParent.getElement(j); int start = bqChild.getStartOffset(); int end = bqChild.getEndOffset(); if(end < editor.getSelectionStart() || start > editor.getSelectionEnd()) elsToIndent.add(bqChild); } } } HTMLDocument doc = (HTMLDocument)editor.getDocument(); adjustListElemsIndent(listElems, doc); blockquoteElements(new ArrayList(elsToIndent), doc); unblockquoteElements(new ArrayList(elsToOutdent), doc); } private void adjustListElemsIndent(List elems, HTMLDocument doc) { Set rootLists = new HashSet(); Set liElems = new HashSet(); for(int i = 0; i < elems.size(); i++) { Element liEl = HTMLUtils.getParent((Element)elems.get(i), HTML.Tag.LI); if(liEl == null) continue; liElems.add(liEl); Element rootList = HTMLUtils.getListParent(liEl); if(rootList != null) { while(HTMLUtils.getListParent(rootList.getParentElement()) != null) { rootList = HTMLUtils.getListParent(rootList.getParentElement()); } rootLists.add(rootList); } } for(Iterator it = rootLists.iterator(); it.hasNext();) { Element rl = (Element)it.next(); String newHtml = buildListHTML(rl, new ArrayList(liElems)); System.err.println(newHtml); try { doc.setInnerHTML(rl, newHtml); } catch(Exception ex) { ex.printStackTrace(); } } } private List getItems(Element list, List selLiElems, int level) { int c = list.getElementCount(); List items = new ArrayList(); for(int i = 0; i < c; i++) { Element e = list.getElement(i); if(e.getName().equals("li")) { ListItem item = new ListItem(); item.listTag = HTML.getTag(list.getName()); item.level = level; if(selLiElems.contains(e)) { if(direction == INDENT) { item.level++; } else { if(item.level > 0) { item.level--; } } } item.html = HTMLUtils.getElementHTML(e, true); items.add(item); } else if(HTMLUtils.getListParent(e) == e) { items.addAll(getItems(e, selLiElems, level + 1)); } } return items; } private String buildListHTML(Element list, List liItems) { List items = getItems(list, liItems, 0); ListItem lastItem = null; StringBuffer html = new StringBuffer(); for(int i = 0; i < items.size(); i++) { ListItem item = (ListItem)items.get(i); if(lastItem != null && (lastItem.level != item.level || !lastItem.listTag.equals(item.listTag))) { if(lastItem.level > item.level) { html.append(openOrCloseList(lastItem.listTag, -1 * (lastItem.level - item.level))); html.append(item.html); } else if(item.level > lastItem.level) { html.append(openOrCloseList(item.listTag, (item.level - lastItem.level))); html.append(item.html); } else { //html.append("</" + lastItem.listTag + ">"); //html.append("<" + item.listTag + ">"); html.append(item.html); } } else { if(lastItem == null) html.append(openOrCloseList(item.listTag, item.level)); html.append(item.html); } lastItem = item; } if(lastItem != null) html.append(openOrCloseList(lastItem.listTag, -1 * lastItem.level)); return html.toString(); } private String openOrCloseList(HTML.Tag ltag, int level) { String tag; if(level < 0) tag = "</" + ltag + ">\n"; else tag = "<" + ltag + ">\n"; int c = Math.abs(level); StringBuffer sb = new StringBuffer(); for(int i = 0; i < c; i++) sb.append(tag); return sb.toString(); } private class ListItem { String html; int level; HTML.Tag listTag; } private List getLeadingTralingListElems(List elems) { Set listElems = new HashSet(); for(int i = 0; i < elems.size(); i++) { Element el = (Element)elems.get(i); if(HTMLUtils.getListParent(el) != null) listElems.add(el); else break; } for(int i = elems.size() - 1; i >= 0 ; i--) { Element el = (Element)elems.get(i); if(HTMLUtils.getListParent(el) != null) listElems.add(el); else break; } return new ArrayList(listElems); } private void indent1(ActionEvent e, JEditorPane editor) { List elems = getParagraphElements(editor); if(elems.size() == 0) return; List listElems = this.getLeadingTralingListElems(elems); elems.removeAll(listElems); HTMLDocument doc = (HTMLDocument)editor.getDocument(); blockquoteElements(elems, doc); adjustListElemsIndent(listElems, doc); } private void indent(ActionEvent e, JEditorPane editor) { List elems = getParagraphElements(editor); if(elems.size() == 0) return; HTMLDocument doc = (HTMLDocument)editor.getDocument(); List nonListElems = new LinkedList(); for(Iterator it = elems.iterator(); it.hasNext();) { Element el = (Element)it.next(); if(HTMLUtils.getListParent(el) == null) { nonListElems.add(el); it.remove(); } } blockquoteElements(nonListElems, doc); //now list elements are left over Map listEls = getListElems(elems); for(Iterator it = listEls.keySet().iterator(); it.hasNext();) { Element listParent = (Element)it.next(); List liElems = (List)listEls.get(listParent); StringBuffer sb = new StringBuffer(); sb.append("<" + listParent.getName() + ">\n"); for(int i = 0; i < liElems.size(); i++) { Element liElem = (Element)liElems.get(i); sb.append(HTMLUtils.getElementHTML(liElem, true)); } sb.append("</" + listParent.getName() + ">\n"); System.err.println(sb); for(int i = liElems.size() - 1; i >= 0; i--) { Element liElem = (Element)liElems.get(i); try { if(i == 0) { doc.setOuterHTML(liElem, sb.toString()); } else { HTMLUtils.removeElement(liElem); } } catch(Exception ble) { ble.printStackTrace(); } } } } private void unblockquoteElements(List elems, HTMLDocument doc) { for(Iterator it = elems.iterator(); it.hasNext();) { Element curE = (Element)it.next(); if(!curE.getName().equals("blockquote")) continue; String eleHtml = HTMLUtils.getElementHTML(curE, false); HTML.Tag t = HTMLUtils.getStartTag(eleHtml); if(t == null || !t.breaksFlow()) eleHtml = "<p>\n" + eleHtml + "</p>\n"; try { doc.setOuterHTML(curE, eleHtml); } catch(Exception ex) { ex.printStackTrace(); } } } private void blockquoteElements(List elems, HTMLDocument doc) { for(Iterator it = elems.iterator(); it.hasNext();) { Element curE = (Element)it.next(); String eleHtml = HTMLUtils.getElementHTML(curE, true); StringBuffer sb = new StringBuffer(); sb.append("<blockquote>\n"); sb.append(eleHtml); sb.append("</blockquote>\n"); try { doc.setOuterHTML(curE, sb.toString()); } catch(Exception ex) { ex.printStackTrace(); } } } public List getParagraphElements(JEditorPane editor) { List elems = new ArrayList(); try { HTMLDocument doc = (HTMLDocument)editor.getDocument(); Element curE = getParaElement(doc, editor.getSelectionStart()); Element endE = getParaElement(doc, editor.getSelectionEnd()); while(curE.getEndOffset() <= endE.getEndOffset()) { elems.add(curE); curE = getParaElement(doc, curE.getEndOffset() + 1); if(curE.getEndOffset() >= doc.getLength()) break; } } catch(ClassCastException cce){} return elems; } private Element getParaElement(HTMLDocument doc, int pos) { Element curE = doc.getParagraphElement(pos); /*while(HTMLUtils.isImplied(curE)) { curE = curE.getParentElement(); }*/ /*Element lp = HTMLUtils.getListParent(curE); if(lp != null) curE = lp;*/ return curE; } /* (non-Javadoc) * @see net.atlanticbb.tantlinger.ui.text.actions.HTMLTextEditAction#wysiwygEditPerformed(java.awt.event.ActionEvent, javax.swing.JEditorPane) */ protected void wysiwygEditPerformed(ActionEvent e, JEditorPane editor) { int cp = editor.getCaretPosition(); CompoundUndoManager.beginCompoundEdit(editor.getDocument()); if(direction == INDENT) indent1(e, editor); else unindent(e, editor); CompoundUndoManager.endCompoundEdit(editor.getDocument()); editor.setCaretPosition(cp); } }