Jskad/source/org/thdl/quilldriver/XMLEditor.java
2003-03-12 12:48:18 +00:00

788 lines
29 KiB
Java

package org.thdl.quilldriver;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Attribute;
import org.jdom.Text;
import org.jdom.DocType;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.List;
import java.util.Set;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.EventObject;
import java.util.EventListener;
import javax.swing.JTextPane;
import javax.swing.text.JTextComponent;
import javax.swing.Action;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import javax.swing.text.Keymap;
import javax.swing.text.Position;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.CaretEvent;
import javax.swing.event.EventListenerList;
public class XMLEditor {
private EventListenerList listenerList = new EventListenerList();
private Document xml;
private JTextPane pane;
private StyledDocument doc;
private DocumentListener docListen;
private Map startOffsets, endOffsets;
private final float indentIncrement = 15.0F;
private final Color tagColor = Color.magenta;
private final Color attColor = Color.pink;
private final Color textColor = Color.darkGray;
private Cursor textCursor;
private Cursor defaultCursor;
private boolean isEditing = false;
private Object editingNode = null;
private boolean hasChanged = false;
private Hashtable actions;
private CaretListener editabilityTracker;
private XMLTagInfo tagInfo;
public XMLEditor(Document xmlDoc, JTextPane textPane, XMLTagInfo tagInfo) {
xml = xmlDoc;
pane = textPane;
this.tagInfo = tagInfo;
startOffsets = new HashMap();
endOffsets = new HashMap();
docListen = new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
hasChanged = true;
}
public void insertUpdate(DocumentEvent e) {
hasChanged = true;
if (getStartOffsetForNode(editingNode) > e.getOffset()) {
javax.swing.text.Document d = e.getDocument();
try {
startOffsets.put(editingNode, d.createPosition(e.getOffset()));
} catch (BadLocationException ble) {
ble.printStackTrace();
}
}
}
public void removeUpdate(DocumentEvent e) {
hasChanged = true;
}
};
render();
textCursor = new Cursor(Cursor.TEXT_CURSOR);
defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
pane.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
JTextPane p = (JTextPane)e.getSource();
int offset = p.viewToModel(e.getPoint());
if (isEditable(offset)) p.setCursor(textCursor);
else p.setCursor(defaultCursor);
}
});
MouseListener[] listeners = (MouseListener[])pane.getListeners(MouseListener.class);
for (int i=0; i<listeners.length; i++) pane.removeMouseListener(listeners[i]);
pane.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
JTextPane p = (JTextPane)e.getSource();
int offset = p.viewToModel(e.getPoint());
if (isEditable(offset)) {
p.requestFocus();
if (isEditing) fireEndEditEvent();
fireStartEditEvent(getNodeForOffset(offset));
p.setCaretPosition(offset);
} else {
Object node = getNodeForOffset(offset);
if (isEditing) fireEndEditEvent();
if (node != null) fireCantEditEvent(getNodeForOffset(offset));
}
}
});
pane.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
JTextPane p = (JTextPane)e.getSource();
if (isEditable(p.getCaretPosition()))
fireStartEditEvent(getNodeForOffset(p.getCaretPosition()));
}
public void focusLost(FocusEvent e) {
JTextPane p = (JTextPane)e.getSource();
if (isEditing) fireEndEditEvent();
}
});
editabilityTracker = new CaretListener() {
public void caretUpdate(CaretEvent e) {
int dot = e.getDot();
if (getNodeForOffset(dot) != getNodeForOffset(e.getMark()))
pane.getCaret().setDot(dot);
if (!isEditable(dot)) {
while (!isEditable(dot) && dot<pane.getDocument().getLength()) dot++;
if (dot == pane.getDocument().getLength()) {
dot = e.getDot();
do {
dot--;
} while (!isEditable(dot) && dot>-1);
if (dot == -1) return; //what to do? there's nothing to edit in this pane
}
if (isEditable(dot)) {
pane.getCaret().setDot(dot);
if (getNodeForOffset(dot) != null) fireStartEditEvent(getNodeForOffset(dot));
}
} else if (editingNode == null) //need to start editing because cursor happens to be on an editable node
fireStartEditEvent(getNodeForOffset(dot));
}
};
Action nextNodeAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
int prePos = p.getCaretPosition();
Object node = getNodeForOffset(prePos);
int i = prePos+1;
while (i<p.getDocument().getLength() && isEditable(i) && getNodeForOffset(i) == node) i++;
while (i<p.getDocument().getLength() && !isEditable(i)) i++;
node = getNodeForOffset(i);
while (i<p.getDocument().getLength() && isEditable(i) && getNodeForOffset(i) == node) i++;
if (isEditing) fireEndEditEvent();
i--;
fireStartEditEvent(getNodeForOffset(i));
p.setCaretPosition(i);
}
};
Action selectNodeAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
Object node = getNodeForOffset(p.getCaretPosition());
if (node != null) {
p.setSelectionStart(((Position)startOffsets.get(node)).getOffset());
int end = ((Position)endOffsets.get(node)).getOffset();
if (node instanceof Text) p.setSelectionEnd(end);
else if (node instanceof Attribute) p.setSelectionEnd(end-1);
}
}
};
Action selForwardAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
int offset = p.getCaretPosition();
Object node = getNodeForOffset(offset);
int last = (((Position)endOffsets.get(node)).getOffset());
if (node instanceof Attribute) last--;
if (offset < last) p.getCaret().moveDot(offset++);
}
};
Action selBackwardAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
int offset = p.getCaretPosition();
int first = (((Position)startOffsets.get(getNodeForOffset(offset))).getOffset());
if (offset > first) p.getCaret().moveDot(offset--);
}
};
Action selectToNodeEndAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
Object node = getNodeForOffset(p.getCaret().getMark());
if (node != null) {
int last = (((Position)endOffsets.get(node)).getOffset());
if (node instanceof Attribute) last--;
p.getCaret().moveDot(last);
}
}
};
Action selectToNodeStartAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
int offset = p.getCaretPosition();
Object node = getNodeForOffset(p.getCaret().getMark());
if (node != null) {
int first = (((Position)startOffsets.get(node)).getOffset());
p.getCaret().moveDot(first);
}
}
};
Action backwardAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
int prePos = p.getCaretPosition();
int newPos = prePos-1;
while (newPos>-1 && !isEditable(newPos)) newPos--;
if (newPos != -1) {
if (getNodeForOffset(prePos) != getNodeForOffset(newPos)) {
fireEndEditEvent();
fireStartEditEvent(getNodeForOffset(newPos));
}
p.setCaretPosition(newPos);
}
}
};
Action forwardAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
int prePos = p.getCaretPosition();
int newPos = prePos+1;
while (newPos<p.getDocument().getLength() && !isEditable(newPos)) newPos++;
if (newPos != p.getDocument().getLength()) {
if (getNodeForOffset(prePos) != getNodeForOffset(newPos)) {
fireEndEditEvent();
fireStartEditEvent(getNodeForOffset(newPos));
}
p.setCaretPosition(newPos);
}
}
};
Action begNodeAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
Object node = getNodeForOffset(p.getCaretPosition());
p.setCaretPosition(((Position)startOffsets.get(node)).getOffset());
}
};
Action endNodeAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
Object node = getNodeForOffset(p.getCaretPosition());
p.setCaretPosition(((Position)endOffsets.get(node)).getOffset());
}
};
Action deleteNextAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
int offset = p.getCaretPosition();
int last = (((Position)endOffsets.get(getNodeForOffset(offset))).getOffset());
if (offset < last) {
StyledDocument d = p.getStyledDocument();
try {
d.remove(offset, 1);
} catch (BadLocationException ble) {
ble.printStackTrace();
}
}
}
};
Action deletePrevAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
int offset = p.getCaretPosition();
int first = (((Position)startOffsets.get(getNodeForOffset(offset))).getOffset());
if (offset > first) {
StyledDocument d = p.getStyledDocument();
try {
d.remove(offset-1, 1);
} catch (BadLocationException ble) {
ble.printStackTrace();
}
}
}
};
Action loseFocusAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
p.transferFocus(); //moves focus to next component
}
};
/* Action selForwardAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTextPane p = (JTextPane)e.getSource();
int offset = p.getCaretPosition();
}
};
*/
createActionTable(pane);
JTextPane tp = new JTextPane();
Keymap keymap = tp.addKeymap("KeyBindings", tp.getKeymap());
KeyStroke[] selectAllKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectAllAction));
if (selectAllKeys != null)
for (int i=0; i<selectAllKeys.length; i++)
keymap.addActionForKeyStroke(selectAllKeys[i], selectNodeAction);
KeyStroke[] selLineKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectLineAction));
if (selLineKeys != null)
for (int i=0; i<selLineKeys.length; i++)
keymap.addActionForKeyStroke(selLineKeys[i], selectNodeAction);
KeyStroke[] selParaKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectParagraphAction));
if (selParaKeys != null)
for (int i=0; i<selParaKeys.length; i++)
keymap.addActionForKeyStroke(selParaKeys[i], selectNodeAction);
KeyStroke[] selEndLineKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectionEndLineAction));
if (selEndLineKeys != null)
for (int i=0; i<selEndLineKeys.length; i++)
keymap.addActionForKeyStroke(selEndLineKeys[i], selectToNodeEndAction);
KeyStroke[] selEndParaKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectionEndParagraphAction));
if (selEndParaKeys != null)
for (int i=0; i<selEndParaKeys.length; i++)
keymap.addActionForKeyStroke(selEndParaKeys[i], selectToNodeEndAction);
KeyStroke[] selBegLineKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectionBeginLineAction));
if (selBegLineKeys != null)
for (int i=0; i<selBegLineKeys.length; i++)
keymap.addActionForKeyStroke(selBegLineKeys[i], selectToNodeStartAction);
KeyStroke[] selBegParaKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectionBeginParagraphAction));
if (selBegParaKeys != null)
for (int i=0; i<selBegParaKeys.length; i++)
keymap.addActionForKeyStroke(selBegParaKeys[i], selectToNodeStartAction);
KeyStroke[] tabKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.insertTabAction));
if (tabKeys != null)
for (int i=0; i<tabKeys.length; i++)
keymap.addActionForKeyStroke(tabKeys[i], nextNodeAction);
KeyStroke enterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
if (enterKey != null) keymap.addActionForKeyStroke(enterKey, nextNodeAction);
KeyStroke[] endLineKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.endLineAction));
if (endLineKeys != null)
for (int i=0; i<endLineKeys.length; i++)
keymap.addActionForKeyStroke(endLineKeys[i], endNodeAction);
KeyStroke[] endParaKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.endParagraphAction));
if (endParaKeys != null)
for (int i=0; i<endParaKeys.length; i++)
keymap.addActionForKeyStroke(endParaKeys[i], endNodeAction);
KeyStroke[] begLineKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.beginLineAction));
if (begLineKeys != null)
for (int i=0; i<begLineKeys.length; i++)
keymap.addActionForKeyStroke(begLineKeys[i], begNodeAction);
KeyStroke[] begParaKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.beginParagraphAction));
if (begParaKeys != null)
for (int i=0; i<begParaKeys.length; i++)
keymap.addActionForKeyStroke(begParaKeys[i], begNodeAction);
KeyStroke[] backwardKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.backwardAction));
if (backwardKeys != null)
for (int i=0; i<backwardKeys.length; i++)
keymap.addActionForKeyStroke(backwardKeys[i], backwardAction);
KeyStroke[] forwardKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.forwardAction));
if (forwardKeys != null)
for (int i=0; i<forwardKeys.length; i++)
keymap.addActionForKeyStroke(forwardKeys[i], forwardAction);
KeyStroke[] delNextKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.deleteNextCharAction));
if (delNextKeys != null)
for (int i=0; i<delNextKeys.length; i++)
keymap.addActionForKeyStroke(delNextKeys[i], deleteNextAction);
KeyStroke[] delPrevKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.deletePrevCharAction));
if (delPrevKeys != null)
for (int i=0; i<delPrevKeys.length; i++)
keymap.addActionForKeyStroke(delPrevKeys[i], deletePrevAction);
KeyStroke[] selForwardKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectionForwardAction));
if (selForwardKeys != null)
for (int i=0; i<selForwardKeys.length; i++)
keymap.addActionForKeyStroke(selForwardKeys[i], selForwardAction);
KeyStroke[] selBackKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectionBackwardAction));
if (selBackKeys != null)
for (int i=0; i<selBackKeys.length; i++)
keymap.addActionForKeyStroke(selBackKeys[i], selBackwardAction);
KeyStroke escapeKey = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
if (escapeKey != null) keymap.addActionForKeyStroke(escapeKey, loseFocusAction);
/*
Actions that still need to be defined:
Fields inherited from class javax.swing.text.DefaultEditorKit
beepAction, beginAction, beginWordAction, copyAction, cutAction,
defaultKeyTypedAction,
downAction, endAction, EndOfLineStringProperty,
endWordAction, insertBreakAction, insertContentAction,
nextWordAction, pageDownAction, pageUpAction,
pasteAction, previousWordAction, readOnlyAction
selectionBeginAction, selectionBeginWordAction,
selectionDownAction, selectionEndAction, selectionEndWordAction,
selectionNextWordAction, selectionPreviousWordAction,
selectionUpAction, selectWordAction,
upAction, writableAction
*/
pane.setKeymap(keymap);
}
private void createActionTable(JTextComponent textComponent) {
actions = new Hashtable();
Action[] actionsArray = textComponent.getActions();
for (int i = 0; i < actionsArray.length; i++) {
Action a = actionsArray[i];
actions.put(a.getValue(Action.NAME), a);
}
}
private Action getActionByName(String name) {
return (Action)(actions.get(name));
}
public void setEditabilityTracker(boolean bool) {
if (bool) {
int p = pane.getCaretPosition();
int q;
if (pane.getDocument().getLength() == 0)
q=0;
else {
if (p>0) q=p-1;
else q=p+1;
}
pane.setCaretPosition(q);
pane.addCaretListener(editabilityTracker); //shouldn't do if already installed
pane.setCaretPosition(p);
}
else pane.removeCaretListener(editabilityTracker);
}
public void updateNode(Object node) {
System.out.println("updating: " + node.toString());
if (node == null)
return;
try {
if (node instanceof Text) {
int p1 = ((Position)startOffsets.get(node)).getOffset();
int p2 = ((Position)endOffsets.get(node)).getOffset();
String val = pane.getDocument().getText(p1, p2-p1).trim();
Text text = (Text)node;
text.setText(val);
} else if (node instanceof Attribute) {
int p1 = ((Position)startOffsets.get(node)).getOffset();
int p2 = ((Position)endOffsets.get(node)).getOffset()-1; //remove right quote
String val = pane.getDocument().getText(p1, p2-p1).trim();
Attribute att = (Attribute)node;
att.setValue(val);
}
System.out.println("updated: " + node.toString());
} catch (BadLocationException ble) {
ble.printStackTrace();
}
}
interface NodeEditListener extends EventListener {
public void nodeEditPerformed(NodeEditEvent ned);
}
public void addNodeEditListener(NodeEditListener ned) {
listenerList.add(NodeEditListener.class, ned);
}
public void removeNodeEditListener(NodeEditListener ned) {
listenerList.remove(NodeEditListener.class, ned);
}
class NodeEditEvent extends EventObject {
Object node;
NodeEditEvent(Object node) {
super(node);
this.node = node;
}
public Object getNode() {
return node;
}
}
class StartEditEvent extends NodeEditEvent {
StartEditEvent(Object node) {
super(node);
}
}
class EndEditEvent extends NodeEditEvent {
public EndEditEvent(Object node) {
super(node);
}
public boolean hasBeenEdited() {
return hasChanged;
}
}
class CantEditEvent extends NodeEditEvent {
public CantEditEvent(Object node) {
super(node);
}
}
public void fireStartEditEvent(Object node) {
//see javadocs on EventListenerList for how following array is structured
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==NodeEditListener.class)
((NodeEditListener)listeners[i+1]).nodeEditPerformed(new StartEditEvent(node));
}
isEditing = true;
editingNode = node;
hasChanged = false;
}
public void fireEndEditEvent() {
if (!isEditing) return;
//see javadocs on EventListenerList for how following array is structured
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==NodeEditListener.class)
((NodeEditListener)listeners[i+1]).nodeEditPerformed(new EndEditEvent(editingNode));
}
if (hasChanged) updateNode(editingNode);
isEditing = false;
editingNode = null;
hasChanged = false;
}
public void fireCantEditEvent(Object node) {
//see javadocs on EventListenerList for how following array is structured
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==NodeEditListener.class)
((NodeEditListener)listeners[i+1]).nodeEditPerformed(new CantEditEvent(node));
}
}
public void setXMLDocument(Document d, String doctype_elementName, String doctype_systemID) {
xml = d;
xml.setDocType(new DocType(doctype_elementName, doctype_systemID));
render();
}
public void render() {
System.out.println("Rendering the document");
doc = pane.getStyledDocument();
int len = doc.getLength();
try {
if (len > 0) doc.remove(0, len);
doc.insertString(0, "\n", null);
} catch (BadLocationException ble) {
ble.printStackTrace();
}
startOffsets.clear();
endOffsets.clear();
Element root = xml.getRootElement();
renderElement(root, 0.0F, doc.getLength());
SimpleAttributeSet eColor = new SimpleAttributeSet();
eColor.addAttribute("xmlnode", root);
doc.setParagraphAttributes(doc.getLength(), 1, eColor, false);
fixOffsets();
doc.addDocumentListener(docListen);
pane.setCaretPosition(0);
setEditabilityTracker(true);
}
public void fixOffsets() {
//replace Integer values in startOffsets and endOffsets with Positions
Set startKeys = startOffsets.keySet();
Iterator iter = startKeys.iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object obj = startOffsets.get(key);
//if (obj instanceof Position)
// startOffsets.put(key, obj); //actually we don't have to do anything here, do we
//since the startoffsets are already set!!
//else
if (obj instanceof Integer) try {
Integer val = (Integer)obj;
startOffsets.put(key, doc.createPosition(val.intValue()));
} catch (BadLocationException ble) {
ble.printStackTrace();
}
}
Set endKeys = endOffsets.keySet();
iter = endKeys.iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object obj = endOffsets.get(key);
//if (obj instanceof Position)
// endOffsets.put(key, obj); //actually we don't have to do anything here, do we
//since the endoffsets are already set!!
//else
if (obj instanceof Integer) try {
Integer val = (Integer)obj;
endOffsets.put(key, doc.createPosition(val.intValue()));
} catch (BadLocationException ble) {
ble.printStackTrace();
}
}
}
public int renderElement(Element e, float indent, int insertOffset) {
try {
Position pos = doc.createPosition(insertOffset);
SimpleAttributeSet eAttributes = new SimpleAttributeSet();
StyleConstants.setLeftIndent(eAttributes, indent);
SimpleAttributeSet eColor = new SimpleAttributeSet();
//StyleConstants.setLeftIndent(eColor, indent);
StyleConstants.setForeground(eColor, tagColor);
eColor.addAttribute("xmlnode", e);
if (pos.getOffset()>0) {
String s = doc.getText(pos.getOffset()-1, 1);
if (s.charAt(0)!='\n') {
AttributeSet attSet = doc.getCharacterElement(pos.getOffset()-1).getAttributes();
doc.insertString(pos.getOffset(), "\n", attSet);
}
}
int start = pos.getOffset();
startOffsets.put(e, new Integer(start));
String tagDisplay;
if (tagInfo == null) tagDisplay = e.getQualifiedName();
else tagDisplay = tagInfo.getTagDisplay(e);
doc.insertString(pos.getOffset(), tagDisplay, eColor); //insert element begin tag
if (tagInfo == null || tagInfo.areTagContentsForDisplay(e.getQualifiedName())) {
List attributes = e.getAttributes();
Iterator iter = attributes.iterator();
while (iter.hasNext()) {
Attribute att = (Attribute)iter.next();
if (tagInfo == null || tagInfo.isAttributeForDisplay(att.getQualifiedName(), e.getQualifiedName()))
renderAttribute(att, pos.getOffset());
}
doc.insertString(pos.getOffset(), ":", eColor);
doc.setParagraphAttributes(start, pos.getOffset()-start, eAttributes, false);
//doc.insertString(pos.getOffset(), "\n", null);
List list = e.getContent();
iter = list.iterator();
while (iter.hasNext()) {
Object next = iter.next();
if (next instanceof Element) {
Element ne = (Element)next;
if (tagInfo == null || tagInfo.isTagForDisplay(ne.getQualifiedName()))
renderElement(ne, indent + indentIncrement, pos.getOffset());
} else if (next instanceof Text) {
Text t = (Text)next;
if (t.getParent().getContent().size() == 1 || t.getTextTrim().length() > 0)
renderText(t, indent + indentIncrement, pos.getOffset());
}
// Also: Comment ProcessingInstruction CDATA EntityRef
}
}
//start = pos.getOffset();
//doc.insertString(start, "}", eColor); //insert element end tag
//doc.setParagraphAttributes(start, pos.getOffset(), eAttributes, false);
if (pos.getOffset()>0) {
//String s = doc.getText(pos.getOffset()-1, 1);
if (doc.getText(pos.getOffset()-1,1).charAt(0)=='\n')
endOffsets.put(e, new Integer(pos.getOffset()-1));
else
endOffsets.put(e, new Integer(pos.getOffset()));
}
//endOffsets.put(e, new Integer(pos.getOffset()));
return pos.getOffset();
//doc.insertString(pos.getOffset(), "\n", null);
} catch (BadLocationException ble) {
ble.printStackTrace();
return -1;
}
}
public int renderAttribute(Attribute att, int insertOffset) {
try {
Position pos = doc.createPosition(insertOffset);
SimpleAttributeSet aColor = new SimpleAttributeSet();
StyleConstants.setForeground(aColor, attColor);
SimpleAttributeSet tColor = new SimpleAttributeSet();
StyleConstants.setForeground(tColor, textColor);
tColor.addAttribute("xmlnode", att);
String name = att.getQualifiedName();
String value = att.getValue();
if (pos.getOffset()>0) {
String s = doc.getText(pos.getOffset()-1, 1);
if (s.charAt(0)!='\n') {
AttributeSet attSet = doc.getCharacterElement(pos.getOffset()-1).getAttributes();
doc.insertString(pos.getOffset(), " ", attSet);
}
}
String displayName;
if (tagInfo == null) displayName = att.getQualifiedName();
else displayName = tagInfo.getAttributeDisplay(att.getQualifiedName(), att.getParent().getQualifiedName());
doc.insertString(pos.getOffset(), displayName+"=", aColor);
startOffsets.put(att, new Integer(pos.getOffset()+1)); //add one so that begin quote is not part of attribute value
doc.insertString(pos.getOffset(), "\"" + att.getValue()+"\"", tColor);
endOffsets.put(att, new Integer(pos.getOffset()));
return pos.getOffset();
} catch (BadLocationException ble) {
ble.printStackTrace();
return -1;
}
}
public int renderText(Text t, float indent, int insertOffset) {
try {
Position pos = doc.createPosition(insertOffset);
SimpleAttributeSet tAttributes = new SimpleAttributeSet();
//StyleConstants.setLeftIndent(tAttributes, indent);
StyleConstants.setForeground(tAttributes, textColor);
tAttributes.addAttribute("xmlnode", t);
doc.insertString(pos.getOffset(), " ", tAttributes); //insert space with text attributes so first character has correct color, xmlnode attribute, etc.
String s = t.getTextTrim();
int start = pos.getOffset();
startOffsets.put(t, new Integer(start));
doc.insertString(pos.getOffset(), s, tAttributes); //insert text
int end = pos.getOffset();
endOffsets.put(t, new Integer(end));
doc.insertString(pos.getOffset(), "\n", tAttributes);
return pos.getOffset();
} catch (BadLocationException ble) {
ble.printStackTrace();
return -1;
}
}
public void removeNode(Object node) {
if (startOffsets.containsKey(node)) { //note: should recursively eliminate all sub-nodes too!!
startOffsets.remove(node);
endOffsets.remove(node);
}
}
public Object getNodeForOffset(int offset) {
AttributeSet attSet = doc.getCharacterElement(offset).getAttributes();
return attSet.getAttribute("xmlnode");
}
public int getStartOffsetForNode(Object node) {
Position pos = (Position)startOffsets.get(node);
if (pos == null) return -1;
else return pos.getOffset();
}
public int getEndOffsetForNode(Object node) {
Position pos = (Position)endOffsets.get(node);
if (pos == null) return -1;
else return pos.getOffset();
}
public boolean isEditable(int offset) {
Object node = getNodeForOffset(offset);
if ((node instanceof Text) &&
(offset<getStartOffsetForNode(node) || offset>getEndOffsetForNode(node)))
return false;
else if (node instanceof Attribute &&
(offset<getStartOffsetForNode(node) || offset>getEndOffsetForNode(node)-1))
return false;
else
return isEditable(node);
}
public boolean isEditable(Object node) {
if (node == null) return false;
else if (node instanceof Element) return false;
else if (node instanceof Text) return true;
else if (node instanceof Attribute) return true;
else return false;
}
public JTextPane getTextPane() {
return pane;
}
public Document getXMLDocument() {
return xml;
}
}