Hallo, ich möche mir ein eigenes StyledDocument für einen WYSIWYG Editor schreiben. Dieses soll direkt auf meinen Content(der in einer ArrayList verwaltet wird) zugreifen. Ich hab mir jetzt schon eine eigene Content implementierung geschrieben, welche die Eingabe richtig in die ArrayList einfügt.
Das Problem vor dem ich aber nun stehe ist das ich, wenn die JTextPane meine StyledDocument implementierung, welche ich mit dem DefaultStyledDocument abgeglichen habe, verwendet ich immer nur das zuletzt getippte Zeichen an dem offset 0 angezeigt bekomme(obwohl es in der ArrayList richtig eingefügt wird).
Das Problem vor dem ich aber nun stehe ist das ich, wenn die JTextPane meine StyledDocument implementierung, welche ich mit dem DefaultStyledDocument abgeglichen habe, verwendet ich immer nur das zuletzt getippte Zeichen an dem offset 0 angezeigt bekomme(obwohl es in der ArrayList richtig eingefügt wird).
Java:
package com.iig.ziawe.gui;
import com.iig.ziawe.ZiaweCore;
import com.iig.ziawe.actions.events.ZiaweDocumentEvent;
import com.iig.ziawe.block.Block;
import com.iig.ziawe.block.FormattedText;
import com.iig.ziawe.utils.Util;
import java.awt.Color;
import java.awt.Font;
import java.awt.font.TextAttribute;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Vector;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.EventListenerList;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.Position;
import javax.swing.text.Segment;
import javax.swing.text.StringContent;
import javax.swing.text.Style;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
import sun.swing.SwingUtilities2;
public class ZiaweDocument implements StyledDocument {
Element root;
StyleContext context;
ZiaweCore core;
BlockContent content;
EventListenerList documentListeners;
EventListenerList undoableListeners;
private Dictionary<Object, Object> documentProperties = null;
/**
* Document property that indicates whether internationalization
* functions such as text reordering or reshaping should be
* performed. This property should not be publicly exposed,
* since it is used for implementation convenience only. As a
* side effect, copies of this property may be in its subclasses
* that live in different packages (e.g. HTMLDocument as of now),
* so those copies should also be taken care of when this property
* needs to be modified.
*/
static final String I18NProperty = "i18n";
/**
* Document property that indicates if a character has been inserted
* into the document that is more than one byte long. GlyphView uses
* this to determine if it should use BreakIterator.
*/
static final Object MultiByteProperty = "multiByte";
public ZiaweDocument() {
FormattedText defaultRoot = new FormattedText(" ");
StyleContext def = StyleContext.getDefaultStyleContext();
defaultRoot.setElemParams(null, new Vector(),
def.getStyle(StyleContext.DEFAULT_STYLE), 0,
this);
core = ZiaweCore.getInstance();
core.getBlockList().add(defaultRoot);
this.content = new BlockContent(core.getBlockList());
this.root = defaultRoot;
this.context = def;
this.putProperty(this.I18NProperty, false);
this.putProperty(this.MultiByteProperty, false);
this.getStartPosition();
}
public Style addStyle(String nm, Style parent) {
return context.addStyle(nm, parent);
}
public void removeStyle(String nm) {
context.removeStyle(nm);
}
public Style getStyle(String nm) {
return context.getStyle(nm);
}
public void setCharacterAttributes(int offset, int length, AttributeSet s,
boolean replace) {
FormattedText fm = this.getFormattedTextFromPosition(offset);
if (fm != null) {
fm.addAttributes(s);
}
//TODO selection formatieren und nicht gesamten block
}
public void setParagraphAttributes(int offset, int length, AttributeSet s,
boolean replace) {
//TODO Step 1: implement code
//TODO Step 2: ???
//TODO Step 3: Profit
this.setCharacterAttributes(offset, length, s, replace);
}
public void setLogicalStyle(int pos, Style s) {
//TODO ???
setCharacterAttributes(pos, 0, s, true);
}
public Style getLogicalStyle(int p) {
FormattedText fm = this.getFormattedTextFromPosition(p);
if (fm != null) {
return (Style) fm.getResolveParent();
}
return null;
}
public Element getParagraphElement(int pos) {
return this.getFormattedTextFromPosition(pos);
}
public Element getCharacterElement(int pos) {
return this.getParagraphElement(pos);
}
public Color getForeground(AttributeSet attr) {
return context.getForeground(attr);
}
public Color getBackground(AttributeSet attr) {
return context.getBackground(attr);
}
public Font getFont(AttributeSet attr) {
return context.getFont(attr);
}
public int getLength() {
return content.length();
}
public void addDocumentListener(DocumentListener listener) {
if (this.documentListeners == null) {
documentListeners = new EventListenerList();
}
this.documentListeners.add(DocumentListener.class, listener);
}
public void removeDocumentListener(DocumentListener listener) {
if (this.documentListeners.getListenerCount() != 0) {
documentListeners.remove(DocumentListener.class, listener);
}
}
public void addUndoableEditListener(UndoableEditListener listener) {
if (this.undoableListeners == null) {
this.undoableListeners = new EventListenerList();
}
this.undoableListeners.add(UndoableEditListener.class, listener);
}
public void removeUndoableEditListener(UndoableEditListener listener) {
if (this.undoableListeners.getListenerCount() != 0) {
undoableListeners.remove(UndoableEditListener.class, listener);
}
}
public Object getProperty(Object key) {
return this.documentProperties.get(key);
}
public void putProperty(Object key, Object value) {
if (value != null) {
this.getDocumentProperties().put(key, value);
}
}
public void remove(int offs, int len) {
try {
content.remove(offs, len);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
public void insertString(int offset, String str, AttributeSet a) {
if (str == null || (str.length() == 0)) {
return;
}
FormattedText fm = this.getFormattedTextFromPosition(offset);
ZiaweDocumentEvent event =
new ZiaweDocumentEvent(offset, str.length(), DocumentEvent.EventType.INSERT,
this);
if (fm != null) {
fm.addAttributes(a);
try {
content.insertString(offset, str);
this.getEndPosition();
if (getProperty(I18NProperty).equals(Boolean.FALSE)) {
// if a default direction of right-to-left has been specified,
// we want complex layout even if the text is all left to right.
Object d = getProperty(TextAttribute.RUN_DIRECTION);
if ((d != null) && (d.equals(TextAttribute.RUN_DIRECTION_RTL))) {
putProperty(I18NProperty, Boolean.TRUE);
} else {
char[] chars = str.toCharArray();
if (SwingUtilities2.isComplexLayout(chars, 0, chars.length)) {
putProperty(I18NProperty, Boolean.TRUE);
}
}
}
insertUpdate(event, a);
fireInsertUpdate(event);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
public String getText(int offset, int length) {
try {
return content.getString(offset, length);
} catch (BadLocationException e) {
e.printStackTrace();
}
return null;
}
public void getText(int offset, int length, Segment txt) {
try {
content.getChars(offset, length, txt);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
public Position getStartPosition() {
return content.createPosition(0);
}
public Position getEndPosition() {
return content.createPosition(content.length() - 1);
}
public Position createPosition(int offs) {
return content.createPosition(offs);
}
public Element[] getRootElements() {
return new Element[] { this.root };
}
public Element getDefaultRootElement() {
return this.root;
}
public void render(Runnable r) {
r.run();
}
private FormattedText getFormattedTextFromPosition(int i) {
Block blck = Util.findBlock(i, core.getBlockList()).getBlock();
if (blck instanceof FormattedText) {
return (FormattedText) blck;
}
return null;
}
public Dictionary<Object, Object> getDocumentProperties() {
if (documentProperties == null) {
documentProperties = new Hashtable(2);
}
return documentProperties;
}
protected void fireInsertUpdate(DocumentEvent e) {
// Guaranteed to return a non-null array
Object[] listeners = this.documentListeners.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == DocumentListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((DocumentListener) listeners[i + 1]).insertUpdate(e);
}
}
}
protected void fireChangedUpdate(DocumentEvent e) {
// Guaranteed to return a non-null array
Object[] listeners = this.documentListeners.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == DocumentListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((DocumentListener) listeners[i + 1]).changedUpdate(e);
}
}
}
protected void fireRemoveUpdate(DocumentEvent e) {
// Guaranteed to return a non-null array
Object[] listeners = this.documentListeners.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == DocumentListener.class) {
// Lazily create the event:
// if (e == null)
// e = new ListSelectionEvent(this, firstIndex, lastIndex);
((DocumentListener) listeners[i + 1]).removeUpdate(e);
}
}
}
/**
* Updates document structure as a result of text insertion. This
* will happen within a write lock. If a subclass of
* this class reimplements this method, it should delegate to the
* superclass as well.
*
* @param chng a description of the change
* @param attr the attributes for the change
*/
protected void insertUpdate(ZiaweDocumentEvent chng, AttributeSet attr) {
// Check if a multi byte is encountered in the inserted text.
if (chng.getType() == DocumentEvent.EventType.INSERT &&
chng.getLength() > 0 &&
!Boolean.TRUE.equals(getProperty(MultiByteProperty))) {
Segment segment = new Segment();
getText(chng.getOffset(), chng.getLength(), segment);
segment.first();
do {
if ((int) segment.current() > 255) {
putProperty(MultiByteProperty, Boolean.TRUE);
break;
}
} while (segment.next() != Segment.DONE);
}
}
}