Index: javax/swing/text/DefaultStyledDocument.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/DefaultStyledDocument.java,v
retrieving revision 1.20
diff -u -r1.20 DefaultStyledDocument.java
--- javax/swing/text/DefaultStyledDocument.java 19 Dec 2005 10:21:39 -0000 1.20
+++ javax/swing/text/DefaultStyledDocument.java 19 Dec 2005 22:36:04 -0000
@@ -48,6 +48,7 @@
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
+import javax.swing.event.UndoableEditEvent;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.UndoableEdit;
@@ -428,6 +429,9 @@
/** Holds the length of structural changes. */
private int length;
+
+ /** Holds the end offset for structural changes. **/
+ private int endOffset;
/**
* The number of inserted end tags. This is a counter which always gets
@@ -675,8 +679,11 @@
public void insert(int offset, int length, ElementSpec[] data,
DefaultDocumentEvent ev)
{
+ if (length == 0)
+ return;
this.offset = offset;
this.length = length;
+ this.endOffset = offset + length;
documentEvent = ev;
// Push the root and the paragraph at offset onto the element stack.
elementStack.clear();
@@ -799,7 +806,7 @@
}
return ret;
}
-
+
/**
* Inserts a content element into the document structure.
*
@@ -810,6 +817,7 @@
prepareContentInsertion();
int len = tag.getLength();
int dir = tag.getDirection();
+ AttributeSet tagAtts = createLeafElement(null, null, 0, 0).getAttributes();
if (dir == ElementSpec.JoinPreviousDirection)
{
// The mauve tests to this class show that a JoinPrevious insertion
@@ -845,45 +853,103 @@
BranchElement paragraph = (BranchElement) elementStack.peek();
int index = paragraph.getElementIndex(offset);
Element current = paragraph.getElement(index);
-
+
Element[] added;
- Element[] removed;
+ Element[] removed = new Element[] {current};
Element[] splitRes = split(current, offset, length);
- // Special case for when offset == startOffset or offset == endOffset.
if (splitRes[0] == null)
{
added = new Element[2];
- added[0] = createLeafElement(paragraph, tag.getAttributes(),
- offset, offset + length);
+ added[0] = createLeafElement(paragraph, tagAtts,
+ offset, endOffset);
added[1] = splitRes[1];
removed = new Element[0];
index++;
}
- else if (current.getStartOffset() == offset)
+ else if (current.getStartOffset() == offset
+ && current.getEndOffset() - 1 == endOffset)
{
- added = new Element[2];
- added[0] = createLeafElement(paragraph, tag.getAttributes(),
- offset, offset + length);
- added[1] = splitRes[1];
- removed = new Element[] { current };
+ // This is if the new insertion covers the entire range of
+ // current
. This will generally happen for the
+ // first insertion into a new paragraph.
+ added = new Element[1];
+ added[0] = createLeafElement(paragraph, tagAtts,
+ offset, endOffset + 1);
}
- else if (current.getEndOffset() - length == offset)
+ else if (current.getStartOffset() == offset)
+ {
+ // This is if the new insertion happens immediately before
+ // the current
Element. In this case, if there is
+ // a split, there are 2 resulting Elements.
+
+ AttributeSet splitAtts = splitRes[1].getAttributes();
+ if (attributeSetsAreSame(tagAtts, splitAtts))
+ {
+ // If the attributes of the adjacent Elements are the same
+ // then we don't split them, we join them into one.
+ added = new Element [1];
+ added[0] = createLeafElement(paragraph, tagAtts, offset,
+ splitRes[1].getEndOffset());
+ }
+ else
+ {
+ // Otherwise we have 2 resulting Elements.
+ added = new Element[2];
+ added[0] = createLeafElement(paragraph, tagAtts,
+ offset, endOffset);
+ added[1] = splitRes[1];
+ }
+ }
+ else if (current.getEndOffset() == endOffset + 1)
{
- added = new Element[2];
- added[0] = splitRes[0];
- added[1] = createLeafElement(paragraph, tag.getAttributes(),
- offset, offset + length);
- removed = new Element[] { current };
+ // This is if the new insertion happens right at the end of
+ // the current
Element. In this case, if there is
+ // a split, there are 2 resulting Elements.
+ AttributeSet splitAtts = splitRes[0].getAttributes();
+ if (attributeSetsAreSame(tagAtts, splitAtts))
+ {
+ // If the attributes are the same, no need to split.
+ added = new Element [1];
+ added[0] = createLeafElement(paragraph, tagAtts,
+ splitRes[0].getStartOffset(),
+ endOffset + 1);
+ }
+ else
+ {
+ // Otherwise there are 2 resulting Elements.
+ added = new Element[2];
+ added[0] = splitRes[0];
+ added[1] = createLeafElement(paragraph, tagAtts, offset,
+ endOffset + 1);
+ }
}
else
{
- added = new Element[3];
- added[0] = splitRes[0];
- added[1] = createLeafElement(paragraph, tag.getAttributes(),
- offset, offset + length);
- added[2] = splitRes[1];
- removed = new Element[] { current };
- }
+ // This is if the new insertion is in the middle of the
+ // current
Element. In this case, if there is a
+ // split, there will be 3 resulting Elements. Note, since
+ // splitRes[0]
and splitRes[1]
were
+ // once the same Element, they have the same attributes.
+ AttributeSet split1Atts = splitRes[0].getAttributes();
+
+ if (attributeSetsAreSame(tagAtts, split1Atts))
+ {
+ // If the attributes are the same, no need to split.
+ added = new Element [1];
+ added[0] = createLeafElement(paragraph, tagAtts,
+ splitRes[0].getStartOffset(),
+ splitRes[1].getEndOffset());
+ }
+ else
+ {
+ // Otherwise there are 3 resulting Elements.
+ added = new Element[3];
+ added[0] = splitRes[0];
+ added[1] = createLeafElement(paragraph, tagAtts, offset,
+ endOffset);
+ added[2] = splitRes[1];
+ }
+ }
paragraph.replace(index, removed.length, added);
addEdit(paragraph, index, removed, added);
}
@@ -1014,7 +1080,7 @@
*/
public String getName()
{
- return "section";
+ return SectionElementName;
}
}
@@ -1233,7 +1299,11 @@
{
Element paragraph = getParagraphElement(position);
AttributeSet attributes = paragraph.getAttributes();
- return (Style) attributes.getResolveParent();
+ AttributeSet a = attributes.getResolveParent();
+ // If the resolve parent is not of type Style, we return null.
+ if (a instanceof Style)
+ return (Style) a;
+ return null;
}
/**
@@ -1302,50 +1372,54 @@
AttributeSet attributes,
boolean replace)
{
- DefaultDocumentEvent ev =
- new DefaultDocumentEvent(offset, length,
- DocumentEvent.EventType.CHANGE);
-
- // Modify the element structure so that the interval begins at an element
- // start and ends at an element end.
- buffer.change(offset, length, ev);
-
- Element root = getDefaultRootElement();
- // Visit all paragraph elements within the specified interval
- int paragraphCount = root.getElementCount();
- for (int pindex = 0; pindex < paragraphCount; pindex++)
+ // Exit early if length is 0, so no DocumentEvent is created or fired.
+ if (length == 0)
+ return;
+ try
{
- Element paragraph = root.getElement(pindex);
- // Skip paragraphs that lie outside the interval.
- if ((paragraph.getStartOffset() > offset + length)
- || (paragraph.getEndOffset() < offset))
- continue;
-
- // Visit content elements within this paragraph
- int contentCount = paragraph.getElementCount();
- for (int cindex = 0; cindex < contentCount; cindex++)
+ // Must obtain a write lock for this method. writeLock() and
+ // writeUnlock() should always be in try/finally block to make
+ // sure that locking happens in a balanced manner.
+ writeLock();
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent(
+ offset,
+ length,
+ DocumentEvent.EventType.CHANGE);
+
+ // Modify the element structure so that the interval begins at an
+ // element
+ // start and ends at an element end.
+ buffer.change(offset, length, ev);
+
+ Element root = getDefaultRootElement();
+ // Visit all paragraph elements within the specified interval
+ int end = offset + length;
+ Element curr;
+ for (int pos = offset; pos < end; )
{
- Element content = paragraph.getElement(cindex);
- // Skip content that lies outside the interval.
- if ((content.getStartOffset() > offset + length)
- || (content.getEndOffset() < offset))
- continue;
-
- if (content instanceof AbstractElement)
- {
- AbstractElement el = (AbstractElement) content;
- if (replace)
- el.removeAttributes(el);
- el.addAttributes(attributes);
- }
- else
- throw new AssertionError("content elements are expected to be"
- + "instances of "
- + "javax.swing.text.AbstractDocument.AbstractElement");
+ // Get the CharacterElement at offset pos.
+ curr = getCharacterElement(pos);
+ if (pos == curr.getEndOffset())
+ break;
+
+ MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes();
+ ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace));
+ // If replace is true, remove all the old attributes.
+ if (replace)
+ a.removeAttributes(a);
+ // Add all the new attributes.
+ a.addAttributes(attributes);
+ // Increment pos so we can check the next CharacterElement.
+ pos = curr.getEndOffset();
}
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ finally
+ {
+ writeUnlock();
}
-
- fireChangedUpdate(ev);
}
/**
@@ -1357,14 +1431,36 @@
public void setLogicalStyle(int position, Style style)
{
Element el = getParagraphElement(position);
- if (el instanceof AbstractElement)
- {
- AbstractElement ael = (AbstractElement) el;
- ael.setResolveParent(style);
- }
- else
- throw new AssertionError("paragraph elements are expected to be"
- + "instances of javax.swing.text.AbstractDocument.AbstractElement");
+ // getParagraphElement doesn't return null but subclasses might so
+ // we check for null here.
+ if (el == null)
+ return;
+ try
+ {
+ writeLock();
+ if (el instanceof AbstractElement)
+ {
+ AbstractElement ael = (AbstractElement) el;
+ ael.setResolveParent(style);
+ int start = el.getStartOffset();
+ int end = el.getEndOffset();
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent (
+ start,
+ end - start,
+ DocumentEvent.EventType.CHANGE);
+ // FIXME: Add an UndoableEdit to this event and fire it.
+ fireChangedUpdate(ev);
+ }
+ else
+ throw new
+ AssertionError("paragraph elements are expected to be"
+ + "instances of AbstractDocument.AbstractElement");
+ }
+ finally
+ {
+ writeUnlock();
+ }
}
/**
@@ -1380,22 +1476,48 @@
AttributeSet attributes,
boolean replace)
{
- writeLock();
- int index = offset;
- while (index < offset + length)
+ try
{
- AbstractElement par = (AbstractElement) getParagraphElement(index);
- // If we have already seen this paragraph element, then exit the loop.
- if (par.getEndOffset() + 1 == index)
- break;
-
- AttributeContext ctx = getAttributeContext();
- if (replace)
- par.removeAttributes(par);
- par.addAttributes(attributes);
- index = par.getEndOffset() + 1;
+ // Must obtain a write lock for this method. writeLock() and
+ // writeUnlock() should always be in try/finally blocks to make
+ // sure that locking occurs in a balanced manner.
+ writeLock();
+
+ // Create a DocumentEvent to use for changedUpdate().
+ DefaultDocumentEvent ev =
+ new DefaultDocumentEvent (
+ offset,
+ length,
+ DocumentEvent.EventType.CHANGE);
+
+ // Have to iterate through all the _paragraph_ elements that are
+ // contained or partially contained in the interval
+ // (offset, offset + length).
+ Element rootElement = getDefaultRootElement();
+ int startElement = rootElement.getElementIndex(offset);
+ int endElement = rootElement.getElementIndex(offset + length - 1);
+ if (endElement < startElement)
+ endElement = startElement;
+
+ for (int i = startElement; i <= endElement; i++)
+ {
+ Element par = rootElement.getElement(i);
+ MutableAttributeSet a = (MutableAttributeSet) par.getAttributes();
+ // Add the change to the DocumentEvent.
+ ev.addEdit(new AttributeUndoableEdit(par, attributes, replace));
+ // If replace is true remove the old attributes.
+ if (replace)
+ a.removeAttributes(a);
+ // Add the new attributes.
+ a.addAttributes(attributes);
+ }
+ fireChangedUpdate(ev);
+ fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+ }
+ finally
+ {
+ writeUnlock();
}
- writeUnlock();
}
/**
@@ -1407,10 +1529,12 @@
*/
protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
{
- super.insertUpdate(ev, attr);
+ super.insertUpdate(ev, attr);
int offset = ev.getOffset();
int length = ev.getLength();
int endOffset = offset + length;
+ AttributeSet paragraphAttributes =
+ getParagraphElement(endOffset).getAttributes();
Segment txt = new Segment();
try
{
@@ -1429,7 +1553,8 @@
Element prev = getCharacterElement(offset);
Element next = getCharacterElement(endOffset);
- for (int i = offset; i < endOffset; ++i)
+ int segmentEnd = txt.offset + txt.count;
+ for (int i = txt.offset; i < segmentEnd; ++i)
{
len++;
if (txt.array[i] == '\n')
@@ -1439,7 +1564,7 @@
// If we are at the last index, then check if we could probably be
// joined with the next element.
- if (i == endOffset - 1)
+ if (i == segmentEnd - 1)
{
if (next.getAttributes().isEqual(attr))
spec.setDirection(ElementSpec.JoinNextDirection);
@@ -1457,13 +1582,12 @@
// Add ElementSpecs for the newline.
ElementSpec endTag = new ElementSpec(null, ElementSpec.EndTagType);
specs.add(endTag);
- ElementSpec startTag = new ElementSpec(null,
+ ElementSpec startTag = new ElementSpec(paragraphAttributes,
ElementSpec.StartTagType);
startTag.setDirection(ElementSpec.JoinFractureDirection);
specs.add(startTag);
len = 0;
- offset += len;
}
}
@@ -1484,7 +1608,7 @@
specs.add(spec);
}
-
+
ElementSpec[] elSpecs =
(ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]);
@@ -1512,6 +1636,20 @@
// Nothing to do here. This is intended to be overridden by subclasses.
}
+ void printElements (Element start, int pad)
+ {
+ for (int i = 0; i < pad; i++)
+ System.out.print(" ");
+ if (pad == 0)
+ System.out.println ("ROOT ELEMENT ("+start.getStartOffset()+", "+start.getEndOffset()+")");
+ else if (start instanceof AbstractDocument.BranchElement)
+ System.out.println ("BranchElement ("+start.getStartOffset()+", "+start.getEndOffset()+")");
+ else
+ System.out.println ("LeafElement ("+start.getStartOffset()+", "+start.getEndOffset()+")");
+ for (int i = 0; i < start.getElementCount(); i ++)
+ printElements (start.getElement(i), pad+3);
+ }
+
/**
* Inserts a bulk of structured content at once.
*
@@ -1545,7 +1683,7 @@
// If there was no content inserted then exit early.
if (length == 0)
return;
-
+
UndoableEdit edit = content.insertString(offset,
contentBuffer.toString());
@@ -1600,4 +1738,9 @@
throw err;
}
}
+
+ static boolean attributeSetsAreSame (AttributeSet a, AttributeSet b)
+ {
+ return (a == null && b == null) || (a != null && a.isEqual(b));
+ }
}