/*
* Created on Feb 26, 2005
*
*/
package net.atlanticbb.tantlinger.ui.text.actions;
import java.awt.Event;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.Action;
import javax.swing.JEditorPane;
import javax.swing.KeyStroke;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import net.atlanticbb.tantlinger.ui.UIUtils;
import net.atlanticbb.tantlinger.ui.text.CompoundUndoManager;
import net.atlanticbb.tantlinger.ui.text.ElementWriter;
import net.atlanticbb.tantlinger.ui.text.HTMLUtils;
import org.bushe.swing.action.ActionManager;
/**
* Action which formats HTML block level elements
*
* @author Bob Tantlinger
*
*/
public class HTMLBlockAction extends HTMLTextEditAction
{
/**
*
*/
private static final long serialVersionUID = 1L;
public static final int DIV = 0;
public static final int P = 1;
public static final int H1 = 2;
public static final int H2 = 3;
public static final int H3 = 4;
public static final int H4 = 5;
public static final int H5 = 6;
public static final int H6 = 7;
public static final int PRE = 8;
public static final int BLOCKQUOTE = 9;
public static final int OL = 10;
public static final int UL = 11;
private static final int KEYS[] =
{
KeyEvent.VK_D, KeyEvent.VK_ENTER, KeyEvent.VK_1, KeyEvent.VK_2,
KeyEvent.VK_3, KeyEvent.VK_4, KeyEvent.VK_5, KeyEvent.VK_6,
KeyEvent.VK_R, KeyEvent.VK_Q, KeyEvent.VK_N, KeyEvent.VK_U
};
public static final String[] ELEMENT_TYPES =
{
i18n.str("body_text"), //$NON-NLS-1$
i18n.str("paragraph"), //$NON-NLS-1$
i18n.str("heading") + " 1", //$NON-NLS-1$ //$NON-NLS-2$
i18n.str("heading") + " 2", //$NON-NLS-1$ //$NON-NLS-2$
i18n.str("heading") + " 3", //$NON-NLS-1$ //$NON-NLS-2$
i18n.str("heading") + " 4", //$NON-NLS-1$ //$NON-NLS-2$
i18n.str("heading") + " 5", //$NON-NLS-1$ //$NON-NLS-2$
i18n.str("heading") + " 6", //$NON-NLS-1$ //$NON-NLS-2$
i18n.str("preformatted"), //$NON-NLS-1$
i18n.str("blockquote"), //$NON-NLS-1$
i18n.str("ordered_list"), //$NON-NLS-1$
i18n.str("unordered_list") //$NON-NLS-1$
};
private int type;
/**
* Creates a new HTMLBlockAction
*
* @param type A block type - P, PRE, BLOCKQUOTE, H1, H2, etc
*
* @throws IllegalArgumentException
*/
public HTMLBlockAction(int type) throws IllegalArgumentException
{
super(""); //$NON-NLS-1$
if(type < 0 || type >= ELEMENT_TYPES.length)
throw new IllegalArgumentException("Illegal argument"); //$NON-NLS-1$
this.type = type;
putValue(NAME, ELEMENT_TYPES[type]);
putValue(Action.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KEYS[type], Event.ALT_MASK));
if(type == P)
putValue(MNEMONIC_KEY, new Integer(i18n.mnem("paragraph"))); //$NON-NLS-1$
else if(type == PRE)
putValue(MNEMONIC_KEY, new Integer(i18n.mnem("preformatted"))); //$NON-NLS-1$
else if(type == BLOCKQUOTE)
putValue(MNEMONIC_KEY, new Integer(i18n.mnem("blockquote"))); //$NON-NLS-1$
else if(type == OL)
{
putValue(SMALL_ICON,
UIUtils.getIcon(UIUtils.X16, "listordered.png")); //$NON-NLS-1$
putValue(MNEMONIC_KEY, new Integer(i18n.mnem("ordered_list"))); //$NON-NLS-1$
}
else if(type == UL)
{
putValue(SMALL_ICON,
UIUtils.getIcon(UIUtils.X16, "listunordered.png")); //$NON-NLS-1$
putValue(MNEMONIC_KEY, new Integer(i18n.mnem("unordered_list"))); //$NON-NLS-1$
}
else
{
String s = type + ""; //$NON-NLS-1$
putValue(Action.MNEMONIC_KEY, new Integer(s.charAt(0)));
}
putValue(ActionManager.BUTTON_TYPE, ActionManager.BUTTON_TYPE_VALUE_RADIO);
putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME));
}
protected void updateWysiwygContextState(JEditorPane ed)
{
HTMLDocument document = (HTMLDocument)ed.getDocument();
Element elem = document.getParagraphElement(ed.getCaretPosition());
String elemName = elem.getName();
if(elemName.equals("p-implied")) //$NON-NLS-1$
elemName = elem.getParentElement().getName();
if(type == DIV && (elemName.equals("div") || elemName.equals("body") || elemName.equals("td"))) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
{
setSelected(true);
}
else if(type == UL)
{
Element listElem = HTMLUtils.getListParent(elem);
setSelected(listElem != null && (listElem.getName().equals("ul"))); //$NON-NLS-1$
}
else if(type == OL)
{
Element listElem = HTMLUtils.getListParent(elem);
setSelected(listElem != null && (listElem.getName().equals("ol"))); //$NON-NLS-1$
}
else if(elemName.equals(getTag().toString().toLowerCase()))
{
setSelected(true);
}
else
{
setSelected(false);
}
}
protected void updateSourceContextState(JEditorPane ed)
{
setSelected(false);
}
protected void sourceEditPerformed(ActionEvent e, JEditorPane editor)
{
String tag = getTag().toString();
String prefix = "\n<" + tag + ">\n\t"; //$NON-NLS-1$ //$NON-NLS-2$
String postfix = "\n</" + tag + ">\n"; //$NON-NLS-1$ //$NON-NLS-2$
if(type == OL || type == UL)
{
prefix += "<li>"; //$NON-NLS-1$
postfix = "</li>" + postfix; //$NON-NLS-1$
}
String sel = editor.getSelectedText();
if(sel == null)
{
editor.replaceSelection(prefix + postfix);
int pos = editor.getCaretPosition() - postfix.length();
if(pos >= 0)
editor.setCaretPosition(pos);
}
else
{
sel = prefix + sel + postfix;
editor.replaceSelection(sel);
}
}
protected void wysiwygEditPerformed(ActionEvent e, JEditorPane editor)
{
HTMLDocument document = (HTMLDocument)editor.getDocument();
int caret = editor.getCaretPosition();
CompoundUndoManager.beginCompoundEdit(document);
try
{
if(type == OL || type == UL)
{
insertList(editor, e);
}
else
{
changeBlockType(editor, e);
}
editor.setCaretPosition(caret);
}
catch(Exception awwCrap)
{
awwCrap.printStackTrace();
}
CompoundUndoManager.endCompoundEdit(document);
}
private HTML.Tag getRootTag(Element elem)
{
HTML.Tag root = HTML.Tag.BODY;
if(HTMLUtils.getParent(elem, HTML.Tag.TD) != null)
root = HTML.Tag.TD;
return root;
}
/*private String cutOutElement(Element el) throws BadLocationException
{
String txt = HTMLUtils.getElementHTML(el, false);
HTMLUtils.removeElement(el);
return txt;
}*/
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 void changeListType(Element listParent, HTML.Tag replaceTag, HTMLDocument document)
{
StringWriter out = new StringWriter();
ElementWriter w = new ElementWriter(out, listParent);
try
{
w.write();
String html = out.toString();
html = html.substring(html.indexOf('>') + 1, html.length());
html = html.substring(0, html.lastIndexOf('<'));
html = '<' + replaceTag.toString() + '>' + html + "</" + replaceTag.toString() + '>'; //$NON-NLS-1$
document.setOuterHTML(listParent, html);
}
catch(Exception idiotic){}
}
private void insertList(JEditorPane editor, ActionEvent e)
throws BadLocationException
{
HTMLDocument document = (HTMLDocument)editor.getDocument();
int caretPos = editor.getCaretPosition();
Element elem = document.getParagraphElement(caretPos);
HTML.Tag parentTag = HTML.getTag(elem.getParentElement().getName());
//check if we need to change the list from one type to another
Element listParent = elem.getParentElement().getParentElement();
HTML.Tag listTag = HTML.getTag(listParent.getName());
if(listTag.equals(HTML.Tag.UL) || listTag.equals(HTML.Tag.OL))
{
HTML.Tag t = HTML.getTag(listParent.getName());
if(type == OL && t.equals(HTML.Tag.UL))
{
changeListType(listParent, HTML.Tag.OL, document);
return;
}
else if(type == UL && listTag.equals(HTML.Tag.OL))
{
changeListType(listParent, HTML.Tag.UL, document);
return;
}
}
if(!parentTag.equals(HTML.Tag.LI))//don't allow nested lists
{
//System.err.println("INSERT LIST");
changeBlockType(editor, e);
}
else//is already a list, so turn off list
{
HTML.Tag root = getRootTag(elem);
String txt = HTMLUtils.getElementHTML(elem, false);
editor.setCaretPosition(elem.getEndOffset());
insertHTML("<p>" + txt + "</p>", HTML.Tag.P, root, e); //$NON-NLS-1$ //$NON-NLS-2$
HTMLUtils.removeElement(elem);
}
}
private void changeBlockType(JEditorPane editor, ActionEvent e)
throws BadLocationException
{
HTMLDocument doc = (HTMLDocument)editor.getDocument();
Element curE = doc.getParagraphElement(editor.getSelectionStart());
Element endE = doc.getParagraphElement(editor.getSelectionEnd());
Element curTD = HTMLUtils.getParent(curE, HTML.Tag.TD);
HTML.Tag tag = getTag();
HTML.Tag rootTag = getRootTag(curE);
String html = ""; //$NON-NLS-1$
if(isListType())
{
html = "<" + getTag() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
tag = HTML.Tag.LI;
}
//a list to hold the elements we want to change
List elToRemove = new ArrayList();
elToRemove.add(curE);
while(true)
{
html += HTMLUtils.createTag(tag,
curE.getAttributes(), HTMLUtils.getElementHTML(curE, false));
if(curE.getEndOffset() >= endE.getEndOffset()
|| curE.getEndOffset() >= doc.getLength())
break;
curE = doc.getParagraphElement(curE.getEndOffset() + 1);
elToRemove.add(curE);
//did we enter a (different) table cell?
Element ckTD = HTMLUtils.getParent(curE, HTML.Tag.TD);
if(ckTD != null && !ckTD.equals(curTD))
break;//stop here so we don't mess up the table
}
if(isListType())
html += "</" + getTag() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
//set the caret to the start of the last selected block element
editor.setCaretPosition(curE.getStartOffset());
//insert our changed block
//we insert first and then remove, because of a bug in jdk 6.0
insertHTML(html, getTag(), rootTag, e);
//now, remove the elements that were changed.
for(Iterator it = elToRemove.iterator(); it.hasNext();)
{
Element c = (Element)it.next();
HTMLUtils.removeElement(c);
}
}
private boolean isListType()
{
return type == OL || type == UL;
}
/**
* Gets the tag
* @return
*/
public HTML.Tag getTag()
{
HTML.Tag tag = HTML.Tag.DIV;
switch(type)
{
case P :
tag = HTML.Tag.P;
break;
case H1 :
tag = HTML.Tag.H1;
break;
case H2 :
tag = HTML.Tag.H2;
break;
case H3 :
tag = HTML.Tag.H3;
break;
case H4 :
tag = HTML.Tag.H4;
break;
case H5 :
tag = HTML.Tag.H5;
break;
case H6 :
tag = HTML.Tag.H6;
break;
case PRE :
tag = HTML.Tag.PRE;
break;
case UL :
tag = HTML.Tag.UL;
break;
case OL :
tag = HTML.Tag.OL;
break;
case BLOCKQUOTE :
tag = HTML.Tag.BLOCKQUOTE;
break;
case DIV :
tag = HTML.Tag.DIV;
break;
}
return tag;
}
}