/* * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text; import java.awt.Color;import java.awt.Font;import java.awt.font.TextAttribute;import java.lang.ref.ReferenceQueue;import java.lang.ref.WeakReference;import java.util.Enumeration;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Stack;import java.util.Vector;import java.util.ArrayList;import java.io.IOException;import java.io.ObjectInputStream;import java.io.Serializable;import javax.swing.event.*;import javax.swing.undo.AbstractUndoableEdit;import javax.swing.undo.CannotRedoException;import javax.swing.undo.CannotUndoException;import javax.swing.undo.UndoableEdit;import javax.swing.SwingUtilities;import static sun.swing.SwingUtilities2.IMPLIED_CR; /** * A document that can be marked up with character and paragraph * styles in a manner similar to the Rich Text Format. The element * structure for this document represents style crossings for * style runs. These style runs are mapped into a paragraph element * structure (which may reside in some other structure). The * style runs break at paragraph boundaries since logical styles are * assigned to paragraph boundaries. * <p> * <strong>Warning:</strong> * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeans<sup><font size="-2">TM</font></sup> * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @author Timothy Prinzing * @see Document * @see AbstractDocument */public class DefaultStyledDocument extends AbstractDocument implements StyledDocument { /** * Constructs a styled document. * * @param c the container for the content * @param styles resources and style definitions which may * be shared across documents */ public DefaultStyledDocument(Content c, StyleContext styles) { super(c, styles); listeningStyles = new Vector<Style>(); buffer = new ElementBuffer(createDefaultRoot()); Style defaultStyle = styles.getStyle(StyleContext.DEFAULT_STYLE); setLogicalStyle(0, defaultStyle); } /** * Constructs a styled document with the default content * storage implementation and a shared set of styles. * * @param styles the styles */ public DefaultStyledDocument(StyleContext styles) { this(new GapContent(BUFFER_SIZE_DEFAULT), styles); } /** * Constructs a default styled document. This buffers * input content by a size of <em>BUFFER_SIZE_DEFAULT</em> * and has a style context that is scoped by the lifetime * of the document and is not shared with other documents. */ public DefaultStyledDocument() { this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext()); } /** * Gets the default root element. * * @return the root * @see Document#getDefaultRootElement */ public Element getDefaultRootElement() { return buffer.getRootElement(); } /** * Initialize the document to reflect the given element * structure (i.e. the structure reported by the * <code>getDefaultRootElement</code> method. If the * document contained any data it will first be removed. */ 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(); } } /** * 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. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offset the starting offset >= 0 * @param data the element data * @exception BadLocationException for an invalid starting offset */ 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(); } } /** * Removes an element from this document. * * <p>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.</p> * * <p>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 <em>before</em> * removing old children. * * <p>Element removal results in two events being fired, the * {@code DocumentEvent} for changes in element structure and {@code * UndoableEditEvent} for changes in document content.</p> * * <p>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.</p> * * <p>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.</p> * * @param elem the element to remove * @throws NullPointerException if the element is {@code null} * @throws IllegalArgumentException if the element could not be removed * @throws IllegalStateException if the element structure is invalid * * @since 1.7 */ public void removeElement(Element elem) { try { writeLock(); removeElementImpl(elem); } finally { writeUnlock(); } } private void removeElementImpl(Element elem) { if (elem.getDocument() != this) { throw new IllegalArgumentException("element doesn't belong to document"); } BranchElement parent = (BranchElement) elem.getParentElement(); if (parent == null) { throw new IllegalArgumentException("can't remove the root element"); } int startOffset = elem.getStartOffset(); int removeFrom = startOffset; int endOffset = elem.getEndOffset(); int removeTo = endOffset; int lastEndOffset = getLength() + 1; Content content = getContent(); boolean atEnd = false; boolean isComposedText = Utilities.isComposedTextElement(elem); if (endOffset >= lastEndOffset) { // element includes the last "\n" character, needs special handling if (startOffset <= 0) { throw new IllegalArgumentException("can't remove the whole content"); } removeTo = lastEndOffset - 1; // last "\n" must not be removed try { if (content.getString(startOffset - 1, 1).charAt(0) == '\n') { removeFrom--; // preceding leaf ends with "\n", remove it } } catch (BadLocationException ble) { // can't happen throw new IllegalStateException(ble); } atEnd = true; } int length = removeTo - removeFrom; DefaultDocumentEvent dde = new DefaultDocumentEvent(removeFrom, length, DefaultDocumentEvent.EventType.REMOVE); UndoableEdit ue = null; // do not leave empty branch elements while (parent.getElementCount() == 1) { elem = parent; parent = (BranchElement) parent.getParentElement(); if (parent == null) { // shouldn't happen throw new IllegalStateException("invalid element structure"); } } Element[] removed = { elem }; Element[] added = {}; int index = parent.getElementIndex(startOffset); parent.replace(index, 1, added); dde.addEdit(new ElementEdit(parent, index, removed, added)); if (length > 0) { try { ue = content.remove(removeFrom, length); if (ue != null) { dde.addEdit(ue); } } catch (BadLocationException ble) { // can only happen if the element structure is severely broken throw new IllegalStateException(ble); } lastEndOffset -= length; } if (atEnd) { // preceding leaf element should be extended to cover orphaned "\n" Element prevLeaf = parent.getElement(parent.getElementCount() - 1); while ((prevLeaf != null) && !prevLeaf.isLeaf()) { prevLeaf = prevLeaf.getElement(prevLeaf.getElementCount() - 1); } if (prevLeaf == null) { // shouldn't happen throw new IllegalStateException("invalid element structure"); } int prevStartOffset = prevLeaf.getStartOffset(); BranchElement prevParent = (BranchElement) prevLeaf.getParentElement(); int prevIndex = prevParent.getElementIndex(prevStartOffset); Element newElem; newElem = createLeafElement(prevParent, prevLeaf.getAttributes(), prevStartOffset, lastEndOffset); Element[] prevRemoved = { prevLeaf }; Element[] prevAdded = { newElem }; prevParent.replace(prevIndex, 1, prevAdded); dde.addEdit(new ElementEdit(prevParent, prevIndex, prevRemoved, prevAdded)); } postRemoveUpdate(dde); dde.end(); fireRemoveUpdate(dde); if (! (isComposedText && (ue != null))) { // do not fire UndoabeEdit event for composed text edit (unsupported) fireUndoableEditUpdate(new UndoableEditEvent(this, dde)); } } /** * 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. * * @param nm the name of the style (must be unique within the * collection of named styles). The name may be null if the style * is unnamed, but the caller is responsible * for managing the reference returned as an unnamed style can't * be fetched by name. An unnamed style may be useful for things * like character attribute overrides such as found in a style * run. * @param parent the parent style. This may be null if unspecified * attributes need not be resolved in some other style. * @return the style */ public Style addStyle(String nm, Style parent) { StyleContext styles = (StyleContext) getAttributeContext(); return styles.addStyle(nm, parent); } /** * Removes a named style previously added to the document. * * @param nm the name of the style to remove */ public void removeStyle(String nm) { StyleContext styles = (StyleContext) getAttributeContext(); styles.removeStyle(nm); } /** * Fetches a named style previously added. * * @param nm the name of the style * @return the style */ public Style getStyle(String nm) { StyleContext styles = (StyleContext) getAttributeContext(); return styles.getStyle(nm); } /** * Fetches the list of of style names. * * @return all the style names */ public Enumeration<?> getStyleNames() { return ((StyleContext) getAttributeContext()).getStyleNames(); } /** * 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. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param pos the offset from the start of the document >= 0 * @param s the logical style to assign to the paragraph, null if none */ 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(); } } } /** * Fetches the logical style assigned to the paragraph * represented by the given position. * * @param p the location to translate to a paragraph * and determine the logical style assigned >= 0. This * is an offset from the start of the document. * @return the style, null if none */ 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; } /** * 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. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offset the offset in the document >= 0 * @param length the length >= 0 * @param s the attributes * @param replace true if the previous attributes should be replaced * before setting the new attributes */ public void setCharacterAttributes(int offset, int length, AttributeSet s, boolean replace) { if (length == 0) { return; } try { writeLock();Lines 1–500 of 2744. Load more
/* * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.text; import java.awt.Color; import java.awt.Font; import java.awt.font.TextAttribute; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import java.util.Vector; import java.util.ArrayList; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import javax.swing.event.*; import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoableEdit; import javax.swing.SwingUtilities; import static sun.swing.SwingUtilities2.IMPLIED_CR; /** * A document that can be marked up with character and paragraph * styles in a manner similar to the Rich Text Format. The element * structure for this document represents style crossings for * style runs. These style runs are mapped into a paragraph element * structure (which may reside in some other structure). The * style runs break at paragraph boundaries since logical styles are * assigned to paragraph boundaries. * <p> * <strong>Warning:</strong> * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing. As of 1.4, support for long term storage * of all JavaBeans<sup><font size="-2">TM</font></sup> * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @author Timothy Prinzing * @see Document * @see AbstractDocument */ public class DefaultStyledDocument extends AbstractDocument implements StyledDocument { /** * Constructs a styled document. * * @param c the container for the content * @param styles resources and style definitions which may * be shared across documents */ public DefaultStyledDocument(Content c, StyleContext styles) { super(c, styles); listeningStyles = new Vector<Style>(); buffer = new ElementBuffer(createDefaultRoot()); Style defaultStyle = styles.getStyle(StyleContext.DEFAULT_STYLE); setLogicalStyle(0, defaultStyle); } /** * Constructs a styled document with the default content * storage implementation and a shared set of styles. * * @param styles the styles */ public DefaultStyledDocument(StyleContext styles) { this(new GapContent(BUFFER_SIZE_DEFAULT), styles); } /** * Constructs a default styled document. This buffers * input content by a size of <em>BUFFER_SIZE_DEFAULT</em> * and has a style context that is scoped by the lifetime * of the document and is not shared with other documents. */ public DefaultStyledDocument() { this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleContext()); } /** * Gets the default root element. * * @return the root * @see Document#getDefaultRootElement */ public Element getDefaultRootElement() { return buffer.getRootElement(); } /** * Initialize the document to reflect the given element * structure (i.e. the structure reported by the * <code>getDefaultRootElement</code> method. If the * document contained any data it will first be removed. */ 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(); } } /** * 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. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offset the starting offset >= 0 * @param data the element data * @exception BadLocationException for an invalid starting offset */ 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(); } } /** * Removes an element from this document. * * <p>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.</p> * * <p>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 <em>before</em> * removing old children. * * <p>Element removal results in two events being fired, the * {@code DocumentEvent} for changes in element structure and {@code * UndoableEditEvent} for changes in document content.</p> * * <p>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.</p> * * <p>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.</p> * * @param elem the element to remove * @throws NullPointerException if the element is {@code null} * @throws IllegalArgumentException if the element could not be removed * @throws IllegalStateException if the element structure is invalid * * @since 1.7 */ public void removeElement(Element elem) { try { writeLock(); removeElementImpl(elem); } finally { writeUnlock(); } } private void removeElementImpl(Element elem) { if (elem.getDocument() != this) { throw new IllegalArgumentException("element doesn't belong to document"); } BranchElement parent = (BranchElement) elem.getParentElement(); if (parent == null) { throw new IllegalArgumentException("can't remove the root element"); } int startOffset = elem.getStartOffset(); int removeFrom = startOffset; int endOffset = elem.getEndOffset(); int removeTo = endOffset; int lastEndOffset = getLength() + 1; Content content = getContent(); boolean atEnd = false; boolean isComposedText = Utilities.isComposedTextElement(elem); if (endOffset >= lastEndOffset) { // element includes the last "\n" character, needs special handling if (startOffset <= 0) { throw new IllegalArgumentException("can't remove the whole content"); } removeTo = lastEndOffset - 1; // last "\n" must not be removed try { if (content.getString(startOffset - 1, 1).charAt(0) == '\n') { removeFrom--; // preceding leaf ends with "\n", remove it } } catch (BadLocationException ble) { // can't happen throw new IllegalStateException(ble); } atEnd = true; } int length = removeTo - removeFrom; DefaultDocumentEvent dde = new DefaultDocumentEvent(removeFrom, length, DefaultDocumentEvent.EventType.REMOVE); UndoableEdit ue = null; // do not leave empty branch elements while (parent.getElementCount() == 1) { elem = parent; parent = (BranchElement) parent.getParentElement(); if (parent == null) { // shouldn't happen throw new IllegalStateException("invalid element structure"); } } Element[] removed = { elem }; Element[] added = {}; int index = parent.getElementIndex(startOffset); parent.replace(index, 1, added); dde.addEdit(new ElementEdit(parent, index, removed, added)); if (length > 0) { try { ue = content.remove(removeFrom, length); if (ue != null) { dde.addEdit(ue); } } catch (BadLocationException ble) { // can only happen if the element structure is severely broken throw new IllegalStateException(ble); } lastEndOffset -= length; } if (atEnd) { // preceding leaf element should be extended to cover orphaned "\n" Element prevLeaf = parent.getElement(parent.getElementCount() - 1); while ((prevLeaf != null) && !prevLeaf.isLeaf()) { prevLeaf = prevLeaf.getElement(prevLeaf.getElementCount() - 1); } if (prevLeaf == null) { // shouldn't happen throw new IllegalStateException("invalid element structure"); } int prevStartOffset = prevLeaf.getStartOffset(); BranchElement prevParent = (BranchElement) prevLeaf.getParentElement(); int prevIndex = prevParent.getElementIndex(prevStartOffset); Element newElem; newElem = createLeafElement(prevParent, prevLeaf.getAttributes(), prevStartOffset, lastEndOffset); Element[] prevRemoved = { prevLeaf }; Element[] prevAdded = { newElem }; prevParent.replace(prevIndex, 1, prevAdded); dde.addEdit(new ElementEdit(prevParent, prevIndex, prevRemoved, prevAdded)); } postRemoveUpdate(dde); dde.end(); fireRemoveUpdate(dde); if (! (isComposedText && (ue != null))) { // do not fire UndoabeEdit event for composed text edit (unsupported) fireUndoableEditUpdate(new UndoableEditEvent(this, dde)); } } /** * 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. * * @param nm the name of the style (must be unique within the * collection of named styles). The name may be null if the style * is unnamed, but the caller is responsible * for managing the reference returned as an unnamed style can't * be fetched by name. An unnamed style may be useful for things * like character attribute overrides such as found in a style * run. * @param parent the parent style. This may be null if unspecified * attributes need not be resolved in some other style. * @return the style */ public Style addStyle(String nm, Style parent) { StyleContext styles = (StyleContext) getAttributeContext(); return styles.addStyle(nm, parent); } /** * Removes a named style previously added to the document. * * @param nm the name of the style to remove */ public void removeStyle(String nm) { StyleContext styles = (StyleContext) getAttributeContext(); styles.removeStyle(nm); } /** * Fetches a named style previously added. * * @param nm the name of the style * @return the style */ public Style getStyle(String nm) { StyleContext styles = (StyleContext) getAttributeContext(); return styles.getStyle(nm); } /** * Fetches the list of of style names. * * @return all the style names */ public Enumeration<?> getStyleNames() { return ((StyleContext) getAttributeContext()).getStyleNames(); } /** * 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. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param pos the offset from the start of the document >= 0 * @param s the logical style to assign to the paragraph, null if none */ 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(); } } } /** * Fetches the logical style assigned to the paragraph * represented by the given position. * * @param p the location to translate to a paragraph * and determine the logical style assigned >= 0. This * is an offset from the start of the document. * @return the style, null if none */ 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; } /** * 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. * <p> * This method is thread safe, although most Swing methods * are not. Please see * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How * to Use Threads</A> for more information. * * @param offset the offset in the document >= 0 * @param length the length >= 0 * @param s the attributes * @param replace true if the previous attributes should be replaced * before setting the new attributes */ public void setCharacterAttributes(int offset, int length, AttributeSet s, boolean replace) { if (length == 0) { return; } try { writeLock();