Index: javax/swing/text/DefaultStyledDocument.java =================================================================== RCS file: /cvsroot/classpath/classpath/javax/swing/text/DefaultStyledDocument.java,v retrieving revision 1.14 diff -u -r1.14 DefaultStyledDocument.java --- javax/swing/text/DefaultStyledDocument.java 30 Oct 2005 18:02:53 -0000 1.14 +++ javax/swing/text/DefaultStyledDocument.java 3 Nov 2005 15:39:35 -0000 @@ -41,9 +41,14 @@ import java.awt.Color; import java.awt.Font; import java.io.Serializable; +import java.util.Enumeration; import java.util.Vector; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.UndoableEdit; /** * The default implementation of address@hidden StyledDocument}. @@ -61,6 +66,87 @@ implements StyledDocument { /** + * An address@hidden UndoableEdit} that can undo attribute changes to an element. + * + * @author Roman Kennke (address@hidden) + */ + public static class AttributeUndoableEdit + extends AbstractUndoableEdit + { + /** + * A copy of the old attributes. + */ + protected AttributeSet copy; + + /** + * The new attributes. + */ + protected AttributeSet newAttributes; + + /** + * If the new attributes replaced the old attributes or if they only were + * added to them. + */ + protected boolean isReplacing; + + /** + * The element that has changed. + */ + protected Element element; + + /** + * Creates a new AttributeUndoableEdit. + * + * @param el the element that changes attributes + * @param newAtts the new attributes + * @param replacing if the new attributes replace the old or only append to + * them + */ + public AttributeUndoableEdit(Element el, AttributeSet newAtts, + boolean replacing) + { + element = el; + newAttributes = newAtts; + isReplacing = replacing; + copy = el.getAttributes().copyAttributes(); + } + + /** + * Undos the attribute change. The copy field is set as + * attributes on element. + */ + public void undo() + { + super.undo(); + AttributeSet atts = element.getAttributes(); + if (atts instanceof MutableAttributeSet) + { + MutableAttributeSet mutable = (MutableAttributeSet) atts; + mutable.removeAttributes(atts); + mutable.addAttributes(copy); + } + } + + /** + * Redos an attribute change. This adds newAttributes to the + * element's attribute set, possibly clearing all attributes + * if isReplacing is true. + */ + public void redo() + { + super.undo(); + AttributeSet atts = element.getAttributes(); + if (atts instanceof MutableAttributeSet) + { + MutableAttributeSet mutable = (MutableAttributeSet) atts; + if (isReplacing) + mutable.removeAttributes(atts); + mutable.addAttributes(newAttributes); + } + } + } + + /** * Carries specification information for new address@hidden Element}s that should * be created in address@hidden ElementBuffer}. This allows the parsing process * to be decoupled from the Element creation process. @@ -714,6 +800,29 @@ } } + /** + * Receives notification when any of the document's style changes and calls + * address@hidden DefaultStyledDocument#styleChanged(Style)}. + * + * @author Roman Kennke (address@hidden) + */ + private class StyleChangeListener + implements ChangeListener + { + + /** + * Receives notification when any of the document's style changes and calls + * address@hidden DefaultStyledDocument#styleChanged(Style)}. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + Style style = (Style) event.getSource(); + styleChanged(style); + } + } + /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 940485415728614849L; @@ -729,6 +838,11 @@ protected DefaultStyledDocument.ElementBuffer buffer; /** + * Listens for changes on this document's styles and notifies styleChanged(). + */ + private StyleChangeListener styleChangeListener; + + /** * Creates a new DefaultStyledDocument. */ public DefaultStyledDocument() @@ -781,7 +895,14 @@ public Style addStyle(String nm, Style parent) { StyleContext context = (StyleContext) getAttributeContext(); - return context.addStyle(nm, parent); + Style newStyle = context.addStyle(nm, parent); + + // Register change listener. + if (styleChangeListener == null) + styleChangeListener = new StyleChangeListener(); + newStyle.addChangeListener(styleChangeListener); + + return newStyle; } /** @@ -1144,5 +1265,93 @@ (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]); buffer.insert(offset, length, elSpecs, ev); - } + } + + /** + * Returns an enumeration of all style names. + * + * @return an enumeration of all style names + */ + public Enumeration getStyleNames() + { + StyleContext context = (StyleContext) getAttributeContext(); + return context.getStyleNames(); + } + + /** + * Called when any of this document's styles changes. + * + * @param style the style that changed + */ + protected void styleChanged(Style style) + { + // Nothing to do here. This is intended to be overridden by subclasses. + } + + /** + * Inserts a bulk of structured content at once. + * + * @param offset the offset at which the content should be inserted + * @param data the actual content spec to be inserted + */ + protected void insert(int offset, ElementSpec[] data) + throws BadLocationException + { + writeLock(); + // First we insert the content. + int index = offset; + for (int i = 0; i < data.length; i++) + { + ElementSpec spec = data[i]; + if (spec.getArray() != null && spec.getLength() > 0) + { + String insertString = new String(spec.getArray(), spec.getOffset(), + spec.getLength()); + content.insertString(index, insertString); + } + index += spec.getLength(); + } + // Update the view structure. + DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, index - offset, + DocumentEvent.EventType.INSERT); + for (int i = 0; i < data.length; i++) + { + ElementSpec spec = data[i]; + AttributeSet atts = spec.getAttributes(); + if (atts != null) + insertUpdate(ev, atts); + } + + // Finally we must update the document structure and fire the insert update + // event. + buffer.insert(offset, index - offset, data, ev); + fireInsertUpdate(ev); + writeUnlock(); + } + + /** + * Initializes the DefaultStyledDocument with the specified + * data. + * + * @param data the specification of the content with which the document is + * initialized + */ + protected void create(ElementSpec[] data) + { + try + { + // Clear content. + content.remove(0, content.length()); + // Clear buffer and root element. + buffer = new ElementBuffer(createDefaultRoot()); + // Insert the data. + insert(0, data); + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError("Unexpected bad location"); + err.initCause(ex); + throw err; + } + } }