Method from javax.swing.text.DefaultStyledDocument Detail: |
public void addDocumentListener(DocumentListener listener) {
synchronized(listeningStyles) {
int oldDLCount = listenerList.getListenerCount
(DocumentListener.class);
super.addDocumentListener(listener);
if (oldDLCount == 0) {
if (styleContextChangeListener == null) {
styleContextChangeListener =
createStyleContextChangeListener();
}
if (styleContextChangeListener != null) {
StyleContext styles = (StyleContext)getAttributeContext();
List< ChangeListener > staleListeners =
AbstractChangeHandler.getStaleListeners(styleContextChangeListener);
for (ChangeListener l: staleListeners) {
styles.removeChangeListener(l);
}
styles.addChangeListener(styleContextChangeListener);
}
updateStylesListeningTo();
}
}
}
Adds a document listener for notification of any changes. |
public Style addStyle(String nm,
Style parent) {
StyleContext styles = (StyleContext) getAttributeContext();
return styles.addStyle(nm, parent);
}
Adds a new style into the logical style hierarchy. Style attributes
resolve from bottom up so an attribute specified in a child
will override an attribute specified in the parent. |
protected void create(ElementSpec[] data) {
try {
if (getLength() != 0) {
remove(0, getLength());
}
writeLock();
// install the content
Content c = getContent();
int n = data.length;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
ElementSpec es = data[i];
if (es.getLength() > 0) {
sb.append(es.getArray(), es.getOffset(), es.getLength());
}
}
UndoableEdit cEdit = c.insertString(0, sb.toString());
// build the event and element structure
int length = sb.length();
DefaultDocumentEvent evnt =
new DefaultDocumentEvent(0, length, DocumentEvent.EventType.INSERT);
evnt.addEdit(cEdit);
buffer.create(length, data, evnt);
// update bidi (possibly)
super.insertUpdate(evnt, null);
// notify the listeners
evnt.end();
fireInsertUpdate(evnt);
fireUndoableEditUpdate(new UndoableEditEvent(this, evnt));
} catch (BadLocationException ble) {
throw new StateInvariantError("problem initializing");
} finally {
writeUnlock();
}
}
Initialize the document to reflect the given element
structure (i.e. the structure reported by the
getDefaultRootElement method. If the
document contained any data it will first be removed. |
protected AbstractElement createDefaultRoot() {
// grabs a write-lock for this initialization and
// abandon it during initialization so in normal
// operation we can detect an illegitimate attempt
// to mutate attributes.
writeLock();
BranchElement section = new SectionElement();
BranchElement paragraph = new BranchElement(section, null);
LeafElement brk = new LeafElement(paragraph, null, 0, 1);
Element[] buff = new Element[1];
buff[0] = brk;
paragraph.replace(0, 0, buff);
buff[0] = paragraph;
section.replace(0, 0, buff);
writeUnlock();
return section;
}
Creates the root element to be used to represent the
default document structure. |
short createSpecsForInsertAfterNewline(Element paragraph,
Element pParagraph,
AttributeSet pattr,
Vector<ElementSpec> parseBuffer,
int offset,
int endOffset) {
// Need to find the common parent of pParagraph and paragraph.
if(paragraph.getParentElement() == pParagraph.getParentElement()) {
// The simple (and common) case that pParagraph and
// paragraph have the same parent.
ElementSpec spec = new ElementSpec(pattr, ElementSpec.EndTagType);
parseBuffer.addElement(spec);
spec = new ElementSpec(pattr, ElementSpec.StartTagType);
parseBuffer.addElement(spec);
if(pParagraph.getEndOffset() != endOffset)
return ElementSpec.JoinFractureDirection;
Element parent = pParagraph.getParentElement();
if((parent.getElementIndex(offset) + 1) < parent.getElementCount())
return ElementSpec.JoinNextDirection;
}
else {
// Will only happen for text with more than 2 levels.
// Find the common parent of a paragraph and pParagraph
Vector< Element > leftParents = new Vector< Element >();
Vector< Element > rightParents = new Vector< Element >();
Element e = pParagraph;
while(e != null) {
leftParents.addElement(e);
e = e.getParentElement();
}
e = paragraph;
int leftIndex = -1;
while(e != null && (leftIndex = leftParents.indexOf(e)) == -1) {
rightParents.addElement(e);
e = e.getParentElement();
}
if(e != null) {
// e identifies the common parent.
// Build the ends.
for(int counter = 0; counter < leftIndex;
counter++) {
parseBuffer.addElement(new ElementSpec
(null, ElementSpec.EndTagType));
}
// And the starts.
ElementSpec spec;
for(int counter = rightParents.size() - 1;
counter >= 0; counter--) {
spec = new ElementSpec(rightParents.elementAt(counter).getAttributes(),
ElementSpec.StartTagType);
if(counter > 0)
spec.setDirection(ElementSpec.JoinNextDirection);
parseBuffer.addElement(spec);
}
// If there are right parents, then we generated starts
// down the right subtree and there will be an element to
// join to.
if(rightParents.size() > 0)
return ElementSpec.JoinNextDirection;
// No right subtree, e.getElement(endOffset) is a
// leaf. There will be a facture.
return ElementSpec.JoinFractureDirection;
}
// else: Could throw an exception here, but should never get here!
}
return ElementSpec.OriginateDirection;
}
This is called by insertUpdate when inserting after a new line.
It generates, in parseBuffer , ElementSpecs that will
position the stack in paragraph .
It returns the direction the last StartSpec should have (this don't
necessarily create the last start spec). |
ChangeListener createStyleChangeListener() {
return new StyleChangeHandler(this);
}
Returns a new instance of StyleChangeHandler. |
ChangeListener createStyleContextChangeListener() {
return new StyleContextChangeHandler(this);
}
Returns a new instance of StyleContextChangeHandler. |
public Color getBackground(AttributeSet attr) {
StyleContext styles = (StyleContext) getAttributeContext();
return styles.getBackground(attr);
}
Gets the background color from an attribute set. |
public Element getCharacterElement(int pos) {
Element e;
for (e = getDefaultRootElement(); ! e.isLeaf(); ) {
int index = e.getElementIndex(pos);
e = e.getElement(index);
}
return e;
}
Gets a character element based on a position. |
public Element getDefaultRootElement() {
return buffer.getRootElement();
}
Gets the default root element. |
public Font getFont(AttributeSet attr) {
StyleContext styles = (StyleContext) getAttributeContext();
return styles.getFont(attr);
}
Gets the font from an attribute set. |
public Color getForeground(AttributeSet attr) {
StyleContext styles = (StyleContext) getAttributeContext();
return styles.getForeground(attr);
}
Gets the foreground color from an attribute set. |
public Style getLogicalStyle(int p) {
Style s = null;
Element paragraph = getParagraphElement(p);
if (paragraph != null) {
AttributeSet a = paragraph.getAttributes();
AttributeSet parent = a.getResolveParent();
if (parent instanceof Style) {
s = (Style) parent;
}
}
return s;
}
Fetches the logical style assigned to the paragraph
represented by the given position. |
public Element getParagraphElement(int pos) {
Element e;
for (e = getDefaultRootElement(); ! e.isLeaf(); ) {
int index = e.getElementIndex(pos);
e = e.getElement(index);
}
if(e != null)
return e.getParentElement();
return e;
}
Gets the paragraph element at the offset pos .
A paragraph consists of at least one child Element, which is usually
a leaf. |
public Style getStyle(String nm) {
StyleContext styles = (StyleContext) getAttributeContext();
return styles.getStyle(nm);
}
Fetches a named style previously added. |
public Enumeration<?> getStyleNames() {
return ((StyleContext) getAttributeContext()).getStyleNames();
}
Fetches the list of of style names. |
protected void insert(int offset,
ElementSpec[] data) throws BadLocationException {
if (data == null || data.length == 0) {
return;
}
try {
writeLock();
// install the content
Content c = getContent();
int n = data.length;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
ElementSpec es = data[i];
if (es.getLength() > 0) {
sb.append(es.getArray(), es.getOffset(), es.getLength());
}
}
if (sb.length() == 0) {
// Nothing to insert, bail.
return;
}
UndoableEdit cEdit = c.insertString(offset, sb.toString());
// create event and build the element structure
int length = sb.length();
DefaultDocumentEvent evnt =
new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.INSERT);
evnt.addEdit(cEdit);
buffer.insert(offset, length, data, evnt);
// update bidi (possibly)
super.insertUpdate(evnt, null);
// notify the listeners
evnt.end();
fireInsertUpdate(evnt);
fireUndoableEditUpdate(new UndoableEditEvent(this, evnt));
} finally {
writeUnlock();
}
}
Inserts new elements in bulk. This is useful to allow
parsing with the document in an unlocked state and
prepare an element structure modification. This method
takes an array of tokens that describe how to update an
element structure so the time within a write lock can
be greatly reduced in an asynchronous update situation.
This method is thread safe, although most Swing methods
are not. Please see
How
to Use Threads for more information. |
protected void insertUpdate(DefaultDocumentEvent chng,
AttributeSet attr) {
int offset = chng.getOffset();
int length = chng.getLength();
if (attr == null) {
attr = SimpleAttributeSet.EMPTY;
}
// Paragraph attributes should come from point after insertion.
// You really only notice this when inserting at a paragraph
// boundary.
Element paragraph = getParagraphElement(offset + length);
AttributeSet pattr = paragraph.getAttributes();
// Character attributes should come from actual insertion point.
Element pParagraph = getParagraphElement(offset);
Element run = pParagraph.getElement(pParagraph.getElementIndex
(offset));
int endOffset = offset + length;
boolean insertingAtBoundry = (run.getEndOffset() == endOffset);
AttributeSet cattr = run.getAttributes();
try {
Segment s = new Segment();
Vector< ElementSpec > parseBuffer = new Vector< ElementSpec >();
ElementSpec lastStartSpec = null;
boolean insertingAfterNewline = false;
short lastStartDirection = ElementSpec.OriginateDirection;
// Check if the previous character was a newline.
if (offset > 0) {
getText(offset - 1, 1, s);
if (s.array[s.offset] == '\n') {
// Inserting after a newline.
insertingAfterNewline = true;
lastStartDirection = createSpecsForInsertAfterNewline
(paragraph, pParagraph, pattr, parseBuffer,
offset, endOffset);
for(int counter = parseBuffer.size() - 1; counter >= 0;
counter--) {
ElementSpec spec = parseBuffer.elementAt(counter);
if(spec.getType() == ElementSpec.StartTagType) {
lastStartSpec = spec;
break;
}
}
}
}
// If not inserting after a new line, pull the attributes for
// new paragraphs from the paragraph under the insertion point.
if(!insertingAfterNewline)
pattr = pParagraph.getAttributes();
getText(offset, length, s);
char[] txt = s.array;
int n = s.offset + s.count;
int lastOffset = s.offset;
for (int i = s.offset; i < n; i++) {
if (txt[i] == '\n') {
int breakOffset = i + 1;
parseBuffer.addElement(
new ElementSpec(attr, ElementSpec.ContentType,
breakOffset - lastOffset));
parseBuffer.addElement(
new ElementSpec(null, ElementSpec.EndTagType));
lastStartSpec = new ElementSpec(pattr, ElementSpec.
StartTagType);
parseBuffer.addElement(lastStartSpec);
lastOffset = breakOffset;
}
}
if (lastOffset < n) {
parseBuffer.addElement(
new ElementSpec(attr, ElementSpec.ContentType,
n - lastOffset));
}
ElementSpec first = parseBuffer.firstElement();
int docLength = getLength();
// Check for join previous of first content.
if(first.getType() == ElementSpec.ContentType &&
cattr.isEqual(attr)) {
first.setDirection(ElementSpec.JoinPreviousDirection);
}
// Do a join fracture/next for last start spec if necessary.
if(lastStartSpec != null) {
if(insertingAfterNewline) {
lastStartSpec.setDirection(lastStartDirection);
}
// Join to the fracture if NOT inserting at the end
// (fracture only happens when not inserting at end of
// paragraph).
else if(pParagraph.getEndOffset() != endOffset) {
lastStartSpec.setDirection(ElementSpec.
JoinFractureDirection);
}
// Join to next if parent of pParagraph has another
// element after pParagraph, and it isn't a leaf.
else {
Element parent = pParagraph.getParentElement();
int pParagraphIndex = parent.getElementIndex(offset);
if((pParagraphIndex + 1) < parent.getElementCount() &&
!parent.getElement(pParagraphIndex + 1).isLeaf()) {
lastStartSpec.setDirection(ElementSpec.
JoinNextDirection);
}
}
}
// Do a JoinNext for last spec if it is content, it doesn't
// already have a direction set, no new paragraphs have been
// inserted or a new paragraph has been inserted and its join
// direction isn't originate, and the element at endOffset
// is a leaf.
if(insertingAtBoundry && endOffset < docLength) {
ElementSpec last = parseBuffer.lastElement();
if(last.getType() == ElementSpec.ContentType &&
last.getDirection() != ElementSpec.JoinPreviousDirection &&
((lastStartSpec == null && (paragraph == pParagraph ||
insertingAfterNewline)) ||
(lastStartSpec != null && lastStartSpec.getDirection() !=
ElementSpec.OriginateDirection))) {
Element nextRun = paragraph.getElement(paragraph.
getElementIndex(endOffset));
// Don't try joining to a branch!
if(nextRun.isLeaf() &&
attr.isEqual(nextRun.getAttributes())) {
last.setDirection(ElementSpec.JoinNextDirection);
}
}
}
// If not inserting at boundary and there is going to be a
// fracture, then can join next on last content if cattr
// matches the new attributes.
else if(!insertingAtBoundry && lastStartSpec != null &&
lastStartSpec.getDirection() ==
ElementSpec.JoinFractureDirection) {
ElementSpec last = parseBuffer.lastElement();
if(last.getType() == ElementSpec.ContentType &&
last.getDirection() != ElementSpec.JoinPreviousDirection &&
attr.isEqual(cattr)) {
last.setDirection(ElementSpec.JoinNextDirection);
}
}
// Check for the composed text element. If it is, merge the character attributes
// into this element as well.
if (Utilities.isComposedTextAttributeDefined(attr)) {
MutableAttributeSet mattr = (MutableAttributeSet) attr;
mattr.addAttributes(cattr);
mattr.addAttribute(AbstractDocument.ElementNameAttribute,
AbstractDocument.ContentElementName);
// Assure that the composed text element is named properly
// and doesn't have the CR attribute defined.
mattr.addAttribute(StyleConstants.NameAttribute,
AbstractDocument.ContentElementName);
if (mattr.isDefined(IMPLIED_CR)) {
mattr.removeAttribute(IMPLIED_CR);
}
}
ElementSpec[] spec = new ElementSpec[parseBuffer.size()];
parseBuffer.copyInto(spec);
buffer.insert(offset, length, spec, chng);
} catch (BadLocationException bl) {
}
super.insertUpdate( chng, attr );
}
Updates document structure as a result of text insertion. This
will happen within a write lock. This implementation simply
parses the inserted content for line breaks and builds up a set
of instructions for the element buffer. |
public void removeDocumentListener(DocumentListener listener) {
synchronized(listeningStyles) {
super.removeDocumentListener(listener);
if (listenerList.getListenerCount(DocumentListener.class) == 0) {
for (int counter = listeningStyles.size() - 1; counter >= 0;
counter--) {
listeningStyles.elementAt(counter).
removeChangeListener(styleChangeListener);
}
listeningStyles.removeAllElements();
if (styleContextChangeListener != null) {
StyleContext styles = (StyleContext)getAttributeContext();
styles.removeChangeListener(styleContextChangeListener);
}
}
}
}
Removes a document listener. |
public void removeElement(Element elem) {
try {
writeLock();
removeElementImpl(elem);
} finally {
writeUnlock();
}
}
Removes an element from this document.
The element is removed from its parent element, as well as
the text in the range identified by the element. If the
element isn't associated with the document, {@code
IllegalArgumentException} is thrown.
As empty branch elements are not allowed in the document, if the
element is the sole child, its parent element is removed as well,
recursively. This means that when replacing all the children of a
particular element, new children should be added before
removing old children.
Element removal results in two events being fired, the
{@code DocumentEvent} for changes in element structure and {@code
UndoableEditEvent} for changes in document content.
If the element contains end-of-content mark (the last {@code
"\n"} character in document), this character is not removed;
instead, preceding leaf element is extended to cover the
character. If the last leaf already ends with {@code "\n",} it is
included in content removal.
If the element is {@code null,} {@code NullPointerException} is
thrown. If the element structure would become invalid after the removal,
for example if the element is the document root element, {@code
IllegalArgumentException} is thrown. If the current element structure is
invalid, {@code IllegalStateException} is thrown. |
public void removeStyle(String nm) {
StyleContext styles = (StyleContext) getAttributeContext();
styles.removeStyle(nm);
}
Removes a named style previously added to the document. |
protected void removeUpdate(DefaultDocumentEvent chng) {
super.removeUpdate(chng);
buffer.remove(chng.getOffset(), chng.getLength(), chng);
}
Updates document structure as a result of text removal. |
public void setCharacterAttributes(int offset,
int length,
AttributeSet s,
boolean replace) {
if (length == 0) {
return;
}
try {
writeLock();
DefaultDocumentEvent changes =
new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.CHANGE);
// split elements that need it
buffer.change(offset, length, changes);
AttributeSet sCopy = s.copyAttributes();
// PENDING(prinz) - this isn't a very efficient way to iterate
int lastEnd;
for (int pos = offset; pos < (offset + length); pos = lastEnd) {
Element run = getCharacterElement(pos);
lastEnd = run.getEndOffset();
if (pos == lastEnd) {
// offset + length beyond length of document, bail.
break;
}
MutableAttributeSet attr = (MutableAttributeSet) run.getAttributes();
changes.addEdit(new AttributeUndoableEdit(run, sCopy, replace));
if (replace) {
attr.removeAttributes(attr);
}
attr.addAttributes(s);
}
changes.end();
fireChangedUpdate(changes);
fireUndoableEditUpdate(new UndoableEditEvent(this, changes));
} finally {
writeUnlock();
}
}
Sets attributes for some part of the document.
A write lock is held by this operation while changes
are being made, and a DocumentEvent is sent to the listeners
after the change has been successfully completed.
This method is thread safe, although most Swing methods
are not. Please see
How
to Use Threads for more information. |
public void setLogicalStyle(int pos,
Style s) {
Element paragraph = getParagraphElement(pos);
if ((paragraph != null) && (paragraph instanceof AbstractElement)) {
try {
writeLock();
StyleChangeUndoableEdit edit = new StyleChangeUndoableEdit((AbstractElement)paragraph, s);
((AbstractElement)paragraph).setResolveParent(s);
int p0 = paragraph.getStartOffset();
int p1 = paragraph.getEndOffset();
DefaultDocumentEvent e =
new DefaultDocumentEvent(p0, p1 - p0, DocumentEvent.EventType.CHANGE);
e.addEdit(edit);
e.end();
fireChangedUpdate(e);
fireUndoableEditUpdate(new UndoableEditEvent(this, e));
} finally {
writeUnlock();
}
}
}
Sets the logical style to use for the paragraph at the
given position. If attributes aren't explicitly set
for character and paragraph attributes they will resolve
through the logical style assigned to the paragraph, which
in turn may resolve through some hierarchy completely
independent of the element hierarchy in the document.
This method is thread safe, although most Swing methods
are not. Please see
How
to Use Threads for more information. |
public void setParagraphAttributes(int offset,
int length,
AttributeSet s,
boolean replace) {
try {
writeLock();
DefaultDocumentEvent changes =
new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.CHANGE);
AttributeSet sCopy = s.copyAttributes();
// PENDING(prinz) - this assumes a particular element structure
Element section = getDefaultRootElement();
int index0 = section.getElementIndex(offset);
int index1 = section.getElementIndex(offset + ((length > 0) ? length - 1 : 0));
boolean isI18N = Boolean.TRUE.equals(getProperty(I18NProperty));
boolean hasRuns = false;
for (int i = index0; i < = index1; i++) {
Element paragraph = section.getElement(i);
MutableAttributeSet attr = (MutableAttributeSet) paragraph.getAttributes();
changes.addEdit(new AttributeUndoableEdit(paragraph, sCopy, replace));
if (replace) {
attr.removeAttributes(attr);
}
attr.addAttributes(s);
if (isI18N && !hasRuns) {
hasRuns = (attr.getAttribute(TextAttribute.RUN_DIRECTION) != null);
}
}
if (hasRuns) {
updateBidi( changes );
}
changes.end();
fireChangedUpdate(changes);
fireUndoableEditUpdate(new UndoableEditEvent(this, changes));
} finally {
writeUnlock();
}
}
Sets attributes for a paragraph.
This method is thread safe, although most Swing methods
are not. Please see
How
to Use Threads for more information. |
protected void styleChanged(Style style) {
// Only propagate change updated if have content
if (getLength() != 0) {
// lazily create a ChangeUpdateRunnable
if (updateRunnable == null) {
updateRunnable = new ChangeUpdateRunnable();
}
// We may get a whole batch of these at once, so only
// queue the runnable if it is not already pending
synchronized(updateRunnable) {
if (!updateRunnable.isPending) {
SwingUtilities.invokeLater(updateRunnable);
updateRunnable.isPending = true;
}
}
}
}
Called when any of this document's styles have changed.
Subclasses may wish to be intelligent about what gets damaged. |
void updateStylesListeningTo() {
synchronized(listeningStyles) {
StyleContext styles = (StyleContext)getAttributeContext();
if (styleChangeListener == null) {
styleChangeListener = createStyleChangeListener();
}
if (styleChangeListener != null && styles != null) {
Enumeration styleNames = styles.getStyleNames();
Vector v = (Vector)listeningStyles.clone();
listeningStyles.removeAllElements();
List< ChangeListener > staleListeners =
AbstractChangeHandler.getStaleListeners(styleChangeListener);
while (styleNames.hasMoreElements()) {
String name = (String)styleNames.nextElement();
Style aStyle = styles.getStyle(name);
int index = v.indexOf(aStyle);
listeningStyles.addElement(aStyle);
if (index == -1) {
for (ChangeListener l: staleListeners) {
aStyle.removeChangeListener(l);
}
aStyle.addChangeListener(styleChangeListener);
}
else {
v.removeElementAt(index);
}
}
for (int counter = v.size() - 1; counter >= 0; counter--) {
Style aStyle = (Style)v.elementAt(counter);
aStyle.removeChangeListener(styleChangeListener);
}
if (listeningStyles.size() == 0) {
styleChangeListener = null;
}
}
}
}
Adds a ChangeListener to new styles, and removes ChangeListener from
old styles. |