summaryrefslogtreecommitdiff
path: root/javax/swing/text
diff options
context:
space:
mode:
Diffstat (limited to 'javax/swing/text')
-rw-r--r--javax/swing/text/AbstractDocument.java203
-rw-r--r--javax/swing/text/BoxView.java263
-rw-r--r--javax/swing/text/CompositeView.java35
-rw-r--r--javax/swing/text/DefaultStyledDocument.java113
-rw-r--r--javax/swing/text/FlowView.java2
-rw-r--r--javax/swing/text/GapContent.java548
-rw-r--r--javax/swing/text/GlyphView.java177
-rw-r--r--javax/swing/text/StyleContext.java22
-rw-r--r--javax/swing/text/Utilities.java117
-rw-r--r--javax/swing/text/html/BlockView.java16
-rw-r--r--javax/swing/text/html/CSS.java9
-rw-r--r--javax/swing/text/html/HTMLDocument.java93
-rw-r--r--javax/swing/text/html/HTMLEditorKit.java203
-rw-r--r--javax/swing/text/html/ImageView.java295
-rw-r--r--javax/swing/text/html/ParagraphView.java16
-rw-r--r--javax/swing/text/html/StyleSheet.java245
-rw-r--r--javax/swing/text/html/TableView.java194
17 files changed, 1673 insertions, 878 deletions
diff --git a/javax/swing/text/AbstractDocument.java b/javax/swing/text/AbstractDocument.java
index 54797fdb0..76f1602f4 100644
--- a/javax/swing/text/AbstractDocument.java
+++ b/javax/swing/text/AbstractDocument.java
@@ -46,6 +46,7 @@ import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.EventListener;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.Vector;
@@ -158,14 +159,10 @@ public abstract class AbstractDocument implements Document, Serializable
private int numReaders = 0;
/**
- * Tells if there are one or more writers waiting.
+ * The number of current writers. If this is > 1 then the same thread entered
+ * the write lock more than once.
*/
- private int numWritersWaiting = 0;
-
- /**
- * A condition variable that readers and writers wait on.
- */
- private Object documentCV = new Object();
+ private int numWriters = 0;
/** An instance of a DocumentFilter.FilterBypass which allows calling
* the insert, remove and replace method without checking for an installed
@@ -315,7 +312,8 @@ public abstract class AbstractDocument implements Document, Serializable
* @throws BadLocationException if <code>offset</code> is not a valid
* location in the documents content model
*/
- public Position createPosition(final int offset) throws BadLocationException
+ public synchronized Position createPosition(final int offset)
+ throws BadLocationException
{
return content.createPosition(offset);
}
@@ -432,7 +430,7 @@ public abstract class AbstractDocument implements Document, Serializable
* @return the thread that currently modifies this <code>Document</code>
* if there is one, otherwise <code>null</code>
*/
- protected final Thread getCurrentWriter()
+ protected final synchronized Thread getCurrentWriter()
{
return currentWriter;
}
@@ -1022,25 +1020,21 @@ public abstract class AbstractDocument implements Document, Serializable
* Blocks until a read lock can be obtained. Must block if there is
* currently a writer modifying the <code>Document</code>.
*/
- public final void readLock()
+ public final synchronized void readLock()
{
- if (currentWriter != null && currentWriter.equals(Thread.currentThread()))
- return;
- synchronized (documentCV)
+ try
{
- while (currentWriter != null || numWritersWaiting > 0)
+ while (currentWriter != null)
{
-
- try
- {
- documentCV.wait();
- }
- catch (InterruptedException ie)
- {
- throw new Error("interrupted trying to get a readLock");
- }
+ if (currentWriter == Thread.currentThread())
+ return;
+ wait();
}
- numReaders++;
+ numReaders++;
+ }
+ catch (InterruptedException ex)
+ {
+ throw new Error("Interrupted during grab read lock");
}
}
@@ -1048,7 +1042,7 @@ public abstract class AbstractDocument implements Document, Serializable
* Releases the read lock. If this was the only reader on this
* <code>Document</code>, writing may begin now.
*/
- public final void readUnlock()
+ public final synchronized void readUnlock()
{
// Note we could have a problem here if readUnlock was called without a
// prior call to readLock but the specs simply warn users to ensure that
@@ -1075,21 +1069,14 @@ public abstract class AbstractDocument implements Document, Serializable
// FIXME: the reference implementation throws a
// javax.swing.text.StateInvariantError here
- if (numReaders == 0)
+ if (numReaders <= 0)
throw new IllegalStateException("document lock failure");
- synchronized (documentCV)
- {
- // If currentWriter is not null, the application code probably had a
- // writeLock and then tried to obtain a readLock, in which case
- // numReaders wasn't incremented
- if (currentWriter == null)
- {
- numReaders --;
- if (numReaders == 0 && numWritersWaiting != 0)
- documentCV.notify();
- }
- }
+ // If currentWriter is not null, the application code probably had a
+ // writeLock and then tried to obtain a readLock, in which case
+ // numReaders wasn't incremented
+ numReaders--;
+ notify();
}
/**
@@ -1113,12 +1100,21 @@ public abstract class AbstractDocument implements Document, Serializable
*/
public void remove(int offset, int length) throws BadLocationException
{
- if (documentFilter == null)
- removeImpl(offset, length);
- else
- documentFilter.remove(getBypass(), offset, length);
+ writeLock();
+ try
+ {
+ DocumentFilter f = getDocumentFilter();
+ if (f == null)
+ removeImpl(offset, length);
+ else
+ f.remove(getBypass(), offset, length);
+ }
+ finally
+ {
+ writeUnlock();
+ }
}
-
+
void removeImpl(int offset, int length) throws BadLocationException
{
// The RI silently ignores all requests that have a negative length.
@@ -1135,21 +1131,12 @@ public abstract class AbstractDocument implements Document, Serializable
new DefaultDocumentEvent(offset, length,
DocumentEvent.EventType.REMOVE);
- try
- {
- writeLock();
-
- // The order of the operations below is critical!
- removeUpdate(event);
- UndoableEdit temp = content.remove(offset, length);
-
- postRemoveUpdate(event);
- fireRemoveUpdate(event);
- }
- finally
- {
- writeUnlock();
- }
+ // The order of the operations below is critical!
+ removeUpdate(event);
+ UndoableEdit temp = content.remove(offset, length);
+
+ postRemoveUpdate(event);
+ fireRemoveUpdate(event);
}
}
@@ -1343,26 +1330,25 @@ public abstract class AbstractDocument implements Document, Serializable
* Blocks until a write lock can be obtained. Must wait if there are
* readers currently reading or another thread is currently writing.
*/
- protected final void writeLock()
+ protected synchronized final void writeLock()
{
- if (currentWriter != null && currentWriter.equals(Thread.currentThread()))
- return;
- synchronized (documentCV)
+ try
{
- numWritersWaiting++;
- while (numReaders > 0)
+ while (numReaders > 0 || currentWriter != null)
{
- try
+ if (Thread.currentThread() == currentWriter)
{
- documentCV.wait();
- }
- catch (InterruptedException ie)
- {
- throw new Error("interruped while trying to obtain write lock");
+ numWriters++;
+ return;
}
+ wait();
}
- numWritersWaiting --;
currentWriter = Thread.currentThread();
+ numWriters = 1;
+ }
+ catch (InterruptedException ex)
+ {
+ throw new Error("Interupted during grab write lock");
}
}
@@ -1370,16 +1356,14 @@ public abstract class AbstractDocument implements Document, Serializable
* Releases the write lock. This allows waiting readers or writers to
* obtain the lock.
*/
- protected final void writeUnlock()
+ protected final synchronized void writeUnlock()
{
- synchronized (documentCV)
- {
- if (Thread.currentThread().equals(currentWriter))
- {
- currentWriter = null;
- documentCV.notifyAll();
- }
- }
+ if (--numWriters <= 0)
+ {
+ numWriters = 0;
+ currentWriter = null;
+ notifyAll();
+ }
}
/**
@@ -2384,6 +2368,11 @@ public abstract class AbstractDocument implements Document, Serializable
/** The serialization UID (compatible with JDK1.5). */
private static final long serialVersionUID = 5230037221564563284L;
+ /**
+ * The threshold that indicates when we switch to using a Hashtable.
+ */
+ private static final int THRESHOLD = 10;
+
/** The starting offset of the change. */
private int offset;
@@ -2394,15 +2383,18 @@ public abstract class AbstractDocument implements Document, Serializable
private DocumentEvent.EventType type;
/**
- * Maps <code>Element</code> to their change records.
+ * Maps <code>Element</code> to their change records. This is only
+ * used when the changes array gets too big. We can use an
+ * (unsync'ed) HashMap here, since changes to this are (should) always
+ * be performed inside a write lock.
*/
- Hashtable changes;
+ private HashMap changes;
/**
* Indicates if this event has been modified or not. This is used to
* determine if this event is thrown.
*/
- boolean modified;
+ private boolean modified;
/**
* Creates a new <code>DefaultDocumentEvent</code>.
@@ -2417,7 +2409,6 @@ public abstract class AbstractDocument implements Document, Serializable
this.offset = offset;
this.length = length;
this.type = type;
- changes = new Hashtable();
modified = false;
}
@@ -2431,9 +2422,27 @@ public abstract class AbstractDocument implements Document, Serializable
public boolean addEdit(UndoableEdit edit)
{
// XXX - Fully qualify ElementChange to work around gcj bug #2499.
- if (edit instanceof DocumentEvent.ElementChange)
+
+ // Start using Hashtable when we pass a certain threshold. This
+ // gives a good memory/performance compromise.
+ if (changes == null && edits.size() > THRESHOLD)
+ {
+ changes = new HashMap();
+ int count = edits.size();
+ for (int i = 0; i < count; i++)
+ {
+ Object o = edits.elementAt(i);
+ if (o instanceof DocumentEvent.ElementChange)
+ {
+ DocumentEvent.ElementChange ec =
+ (DocumentEvent.ElementChange) o;
+ changes.put(ec.getElement(), ec);
+ }
+ }
+ }
+
+ if (changes != null && edit instanceof DocumentEvent.ElementChange)
{
- modified = true;
DocumentEvent.ElementChange elEdit =
(DocumentEvent.ElementChange) edit;
changes.put(elEdit.getElement(), elEdit);
@@ -2492,7 +2501,27 @@ public abstract class AbstractDocument implements Document, Serializable
public DocumentEvent.ElementChange getChange(Element elem)
{
// XXX - Fully qualify ElementChange to work around gcj bug #2499.
- return (DocumentEvent.ElementChange) changes.get(elem);
+ DocumentEvent.ElementChange change = null;
+ if (changes != null)
+ {
+ change = (DocumentEvent.ElementChange) changes.get(elem);
+ }
+ else
+ {
+ int count = edits.size();
+ for (int i = 0; i < count && change == null; i++)
+ {
+ Object o = edits.get(i);
+ if (o instanceof DocumentEvent.ElementChange)
+ {
+ DocumentEvent.ElementChange ec =
+ (DocumentEvent.ElementChange) o;
+ if (elem.equals(ec.getElement()))
+ change = ec;
+ }
+ }
+ }
+ return change;
}
/**
diff --git a/javax/swing/text/BoxView.java b/javax/swing/text/BoxView.java
index 962d06219..72bc07e75 100644
--- a/javax/swing/text/BoxView.java
+++ b/javax/swing/text/BoxView.java
@@ -38,6 +38,7 @@ exception statement from your version. */
package javax.swing.text;
+import java.awt.Container;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
@@ -105,6 +106,8 @@ public class BoxView
myAxis = axis;
layoutValid[0] = false;
layoutValid[1] = false;
+ requirementsValid[X_AXIS] = false;
+ requirementsValid[Y_AXIS] = false;
span[0] = 0;
span[1] = 0;
requirements[0] = new SizeRequirements();
@@ -141,7 +144,10 @@ public class BoxView
*/
public void setAxis(int axis)
{
+ boolean changed = axis != myAxis;
myAxis = axis;
+ if (changed)
+ preferenceChanged(null, true, true);
}
/**
@@ -222,35 +228,20 @@ public class BoxView
*/
public void replace(int offset, int length, View[] views)
{
- int oldNumChildren = getViewCount();
-
// Actually perform the replace.
super.replace(offset, length, views);
// Resize and copy data for cache arrays.
int newItems = views != null ? views.length : 0;
- int delta = newItems - length;
- int src = offset + length;
- int numMove = oldNumChildren - src;
- int dst = src + delta;
- offsets[X_AXIS] = replaceLayoutArray(offsets[X_AXIS], offset,
- oldNumChildren, delta, src, dst,
- numMove);
- spans[X_AXIS] = replaceLayoutArray(spans[X_AXIS], offset,
- oldNumChildren, delta, src, dst,
- numMove);
- offsets[Y_AXIS] = replaceLayoutArray(offsets[Y_AXIS], offset,
- oldNumChildren, delta, src, dst,
- numMove);
- spans[Y_AXIS] = replaceLayoutArray(spans[Y_AXIS], offset,
- oldNumChildren, delta, src, dst,
- numMove);
-
- // Invalidate layout information.
- layoutValid[X_AXIS] = false;
- requirementsValid[X_AXIS] = false;
- layoutValid[Y_AXIS] = false;
- requirementsValid[Y_AXIS] = false;
+ int minor = 1 - myAxis;
+ offsets[myAxis] = replaceLayoutArray(offsets[myAxis], offset, newItems);
+ spans[myAxis] = replaceLayoutArray(spans[myAxis], offset, newItems);
+ layoutValid[myAxis] = false;
+ requirementsValid[myAxis] = false;
+ offsets[minor] = replaceLayoutArray(offsets[minor], offset, newItems);
+ spans[minor] = replaceLayoutArray(spans[minor], offset, newItems);
+ layoutValid[minor] = false;
+ requirementsValid[minor] = false;
}
/**
@@ -261,27 +252,25 @@ public class BoxView
*
* @return the replaced array
*/
- private int[] replaceLayoutArray(int[] oldArray, int offset, int numChildren,
- int delta, int src, int dst, int numMove)
+ private int[] replaceLayoutArray(int[] oldArray, int offset, int newItems)
{
- int[] newArray;
- if (numChildren + delta > oldArray.length)
- {
- int newLength = Math.max(2 * oldArray.length, numChildren + delta);
- newArray = new int[newLength];
- System.arraycopy(oldArray, 0, newArray, 0, offset);
- System.arraycopy(oldArray, src, newArray, dst, numMove);
- }
- else
- {
- newArray = oldArray;
- System.arraycopy(newArray, src, newArray, dst, numMove);
- }
+ int num = getViewCount();
+ int[] newArray = new int[num];
+ System.arraycopy(oldArray, 0, newArray, 0, offset);
+ System.arraycopy(oldArray, offset, newArray, offset + newItems,
+ num - newItems - offset);
return newArray;
}
/**
+ * A Rectangle instance to be reused in the paint() method below.
+ */
+ private final Rectangle tmpRect = new Rectangle();
+
+ private Rectangle clipRect = new Rectangle();
+
+ /**
* Renders the <code>Element</code> that is associated with this
* <code>View</code>.
*
@@ -290,26 +279,95 @@ public class BoxView
*/
public void paint(Graphics g, Shape a)
{
- Rectangle alloc;
- if (a instanceof Rectangle)
- alloc = (Rectangle) a;
- else
- alloc = a.getBounds();
-
- int x = alloc.x + getLeftInset();
- int y = alloc.y + getTopInset();
+ // Try to avoid allocation if possible (almost all cases).
+ Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+
+ // This returns a cached instance.
+ alloc = getInsideAllocation(alloc);
+
+ // The following algorithm optimizes painting of a BoxView by taking
+ // advantage of the layout order of the box children.
+ //
+ // 1. It first searches a child that which's allocation is inside the clip.
+ // This is accomplished by an efficient binary search. This assumes
+ // that the children of the BoxView are laid out in the same order
+ // as their index within the view. This is true for the BoxView, but
+ // might not be the case for all subclasses.
+ // 2. Starting from the found view, it paints the children in both
+ // directions until the first view is hit that is outside the clip.
+
+ // First we search a child view that is inside the clip.
+
+ // Fetch the clip rect and calculate the center point of it.
+ clipRect = g.getClipBounds(clipRect);
+ int cX = clipRect.x + clipRect.width / 2;
+ int cY = clipRect.y + clipRect.height / 2;
+
+ int viewCount = getViewCount();
+ int up = viewCount;
+ int low = 0;
+ int mid = (up - low) / 2;
+ View start = getView(mid);
+
+ int newMid;
+ // Use another cached instance here to avoid allocations during
+ // painting.
+ tmpRect.setBounds(alloc);
+ // This modifies tmpRect.
+ childAllocation(mid, tmpRect);
+ while (! clipRect.intersects(tmpRect))
+ {
+ if (isBefore(cX, cY, tmpRect))
+ {
+ up = mid;
+ newMid = (up - low) / 2 + low;
+ mid = (newMid == mid) ? newMid - 1 : newMid;
+ }
+ else if (isAfter(cX, cY, tmpRect))
+ {
+ low = mid;
+ newMid = (up - low) / 2 + low;
+ mid = (newMid == mid) ? newMid + 1 : newMid;
+ }
+ else
+ break;
+ if (mid >= 0 && mid < viewCount)
+ {
+ start = getView(mid);
+ tmpRect.setBounds(alloc);
+ childAllocation(mid, tmpRect);
+ }
+ else
+ break;
+ }
- Rectangle clip = g.getClipBounds();
- Rectangle tmp = new Rectangle();
- int count = getViewCount();
- for (int i = 0; i < count; ++i)
+ if (mid >= 0 && mid < viewCount)
{
- tmp.x = x + getOffset(X_AXIS, i);
- tmp.y = y + getOffset(Y_AXIS, i);
- tmp.width = getSpan(X_AXIS, i);
- tmp.height = getSpan(Y_AXIS, i);
- if (tmp.intersects(clip))
- paintChild(g, tmp, i);
+ // Ok, we found one view that is inside the clip rect. Now paint the
+ // children before it that are inside the clip.
+ boolean inClip = true;
+ for (int i = mid - 1; i >= 0 && inClip; i--)
+ {
+ start = getView(i);
+ tmpRect.setBounds(alloc);
+ childAllocation(i, tmpRect);
+ inClip = clipRect.intersects(tmpRect);
+ if (inClip)
+ paintChild(g, tmpRect, i);
+ }
+
+ // Now paint the found view and all views after it that lie inside the
+ // clip.
+ inClip = true;
+ for (int i = mid; i < viewCount && inClip; i++)
+ {
+ start = getView(i);
+ tmpRect.setBounds(alloc);
+ childAllocation(i, tmpRect);
+ inClip = clipRect.intersects(tmpRect);
+ if (inClip)
+ paintChild(g, tmpRect, i);
+ }
}
}
@@ -742,49 +800,32 @@ public class BoxView
*/
protected void layout(int width, int height)
{
- int[] newSpan = new int[]{ width, height };
- int count = getViewCount();
-
- // Update minor axis as appropriate. We need to first update the minor
- // axis layout because that might affect the children's preferences along
- // the major axis.
- int minorAxis = myAxis == X_AXIS ? Y_AXIS : X_AXIS;
- if ((! isLayoutValid(minorAxis)) || newSpan[minorAxis] != span[minorAxis])
- {
- layoutValid[minorAxis] = false;
- span[minorAxis] = newSpan[minorAxis];
- layoutMinorAxis(span[minorAxis], minorAxis, offsets[minorAxis],
- spans[minorAxis]);
-
- // Update the child view's sizes.
- for (int i = 0; i < count; ++i)
- {
- getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
- }
- layoutValid[minorAxis] = true;
- }
-
+ layoutAxis(X_AXIS, width);
+ layoutAxis(Y_AXIS, height);
+ }
- // Update major axis as appropriate.
- if ((! isLayoutValid(myAxis)) || newSpan[myAxis] != span[myAxis])
+ private void layoutAxis(int axis, int s)
+ {
+ if (span[axis] != s)
+ layoutValid[axis] = false;
+ if (! layoutValid[axis])
{
- layoutValid[myAxis] = false;
- span[myAxis] = newSpan[myAxis];
- layoutMajorAxis(span[myAxis], myAxis, offsets[myAxis],
- spans[myAxis]);
+ span[axis] = s;
+ updateRequirements(axis);
+ if (axis == myAxis)
+ layoutMajorAxis(span[axis], axis, offsets[axis], spans[axis]);
+ else
+ layoutMinorAxis(span[axis], axis, offsets[axis], spans[axis]);
+ layoutValid[axis] = true;
- // Update the child view's sizes.
- for (int i = 0; i < count; ++i)
+ // Push out child layout.
+ int viewCount = getViewCount();
+ for (int i = 0; i < viewCount; i++)
{
- getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
+ View v = getView(i);
+ v.setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
}
- layoutValid[myAxis] = true;
}
-
- if (layoutValid[myAxis] == false)
- System.err.println("WARNING: Major axis layout must be valid after layout");
- if (layoutValid[minorAxis] == false)
- System.err.println("Minor axis layout must be valid after layout");
}
/**
@@ -807,7 +848,7 @@ public class BoxView
{
View child = getView(i);
spans[i] = (int) child.getPreferredSpan(axis);
- sumPref = spans[i];
+ sumPref += spans[i];
}
// Try to adjust the spans so that we fill the targetSpan.
@@ -1048,9 +1089,11 @@ public class BoxView
{
if (axis != X_AXIS && axis != Y_AXIS)
throw new IllegalArgumentException("Illegal axis argument");
- int weight = 1;
- if (axis == myAxis)
- weight = 0;
+ updateRequirements(axis);
+ int weight = 0;
+ if ((requirements[axis].preferred != requirements[axis].minimum)
+ || (requirements[axis].preferred != requirements[axis].maximum))
+ weight = 1;
return weight;
}
@@ -1077,8 +1120,30 @@ public class BoxView
protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e,
Shape a, ViewFactory vf)
{
- // FIXME: What to do here?
+ boolean wasValid = isLayoutValid(myAxis);
super.forwardUpdate(ec, e, a, vf);
+ // Trigger repaint when one of the children changed the major axis.
+ if (wasValid && ! isLayoutValid(myAxis))
+ {
+ Container c = getContainer();
+ if (a != null && c != null)
+ {
+ int pos = e.getOffset();
+ int index = getViewIndexAtPosition(pos);
+ Rectangle r = getInsideAllocation(a);
+ if (myAxis == X_AXIS)
+ {
+ r.x += offsets[myAxis][index];
+ r.width -= offsets[myAxis][index];
+ }
+ else
+ {
+ r.y += offsets[myAxis][index];
+ r.height -= offsets[myAxis][index];
+ }
+ c.repaint(r.x, r.y, r.width, r.height);
+ }
+ }
}
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias)
diff --git a/javax/swing/text/CompositeView.java b/javax/swing/text/CompositeView.java
index d4467f314..570fc955c 100644
--- a/javax/swing/text/CompositeView.java
+++ b/javax/swing/text/CompositeView.java
@@ -68,7 +68,7 @@ public abstract class CompositeView
* initialized in {@link #getInsideAllocation} and reused and modified in
* {@link #childAllocation(int, Rectangle)}.
*/
- Rectangle insideAllocation;
+ private final Rectangle insideAllocation = new Rectangle();
/**
* The insets of this <code>CompositeView</code>. This is initialized
@@ -282,12 +282,12 @@ public abstract class CompositeView
}
}
}
- else
- {
- throw new BadLocationException("Position " + pos
- + " is not represented by view.", pos);
- }
}
+
+ if (ret == null)
+ throw new BadLocationException("Position " + pos
+ + " is not represented by view.", pos);
+
return ret;
}
@@ -527,24 +527,17 @@ public abstract class CompositeView
if (a == null)
return null;
- Rectangle alloc = a.getBounds();
+ // Try to avoid allocation of Rectangle here.
+ Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+
// Initialize the inside allocation rectangle. This is done inside
// a synchronized block in order to avoid multiple threads creating
// this instance simultanously.
- Rectangle inside;
- synchronized(this)
- {
- inside = insideAllocation;
- if (inside == null)
- {
- inside = new Rectangle();
- insideAllocation = inside;
- }
- }
- inside.x = alloc.x + left;
- inside.y = alloc.y + top;
- inside.width = alloc.width - left - right;
- inside.height = alloc.height - top - bottom;
+ Rectangle inside = insideAllocation;
+ inside.x = alloc.x + getLeftInset();
+ inside.y = alloc.y + getTopInset();
+ inside.width = alloc.width - getLeftInset() - getRightInset();
+ inside.height = alloc.height - getTopInset() - getBottomInset();
return inside;
}
diff --git a/javax/swing/text/DefaultStyledDocument.java b/javax/swing/text/DefaultStyledDocument.java
index 367666053..3156ca67f 100644
--- a/javax/swing/text/DefaultStyledDocument.java
+++ b/javax/swing/text/DefaultStyledDocument.java
@@ -683,6 +683,58 @@ public class DefaultStyledDocument extends AbstractDocument implements
return ret;
}
+ /**
+ * Creates a document in response to a call to
+ * {@link DefaultStyledDocument#create(ElementSpec[])}.
+ *
+ * @param len the length of the inserted text
+ * @param data the specs for the elements
+ * @param ev the document event
+ */
+ void create(int len, ElementSpec[] data, DefaultDocumentEvent ev)
+ {
+ prepareEdit(offset, len);
+ Element el = root;
+ int index = el.getElementIndex(0);
+ while (! el.isLeaf())
+ {
+ Element child = el.getElement(index);
+ Edit edit = new Edit(el, index, false);
+ elementStack.push(edit);
+ el = child;
+ index = el.getElementIndex(0);
+ }
+ Edit ed = (Edit) elementStack.peek();
+ Element child = ed.e.getElement(ed.index);
+ ed.added.add(createLeafElement(ed.e, child.getAttributes(), getLength(),
+ child.getEndOffset()));
+ ed.removed.add(child);
+ while (elementStack.size() > 1)
+ pop();
+ int n = data.length;
+
+ // Reset root element's attributes.
+ AttributeSet newAtts = null;
+ if (n > 0 && data[0].getType() == ElementSpec.StartTagType)
+ newAtts = data[0].getAttributes();
+ if (newAtts == null)
+ newAtts = SimpleAttributeSet.EMPTY;
+ MutableAttributeSet mAtts = (MutableAttributeSet) root.getAttributes();
+ ev.addEdit(new AttributeUndoableEdit(root, newAtts, true));
+ mAtts.removeAttributes(mAtts);
+ mAtts.addAttributes(newAtts);
+
+ // Insert the specified elements.
+ for (int i = 1; i < n; i++)
+ insertElement(data[i]);
+
+ // Pop remaining stack.
+ while (elementStack.size() > 0)
+ pop();
+
+ finishEdit(ev);
+ }
+
private boolean canJoin(Element e0, Element e1)
{
boolean ret = false;
@@ -987,6 +1039,8 @@ public class DefaultStyledDocument extends AbstractDocument implements
ElementEdit ee = new ElementEdit(parent, index, removed, added);
ev.addEdit(ee);
}
+ edits.clear();
+ elementStack.clear();
}
/**
@@ -1034,7 +1088,7 @@ public class DefaultStyledDocument extends AbstractDocument implements
createFracture(data);
i = 0;
}
-
+
// Handle each ElementSpec individually.
for (; i < data.length; i++)
{
@@ -1069,14 +1123,13 @@ public class DefaultStyledDocument extends AbstractDocument implements
if (offset == 0 && fracturedParent != null
&& data[0].getType() == ElementSpec.EndTagType)
{
- for (int p = 0;
+ int p;
+ for (p = 0;
p < data.length && data[p].getType() == ElementSpec.EndTagType;
- p++)
- {
- Edit edit = insertPath[insertPath.length - p - 1];
- edit.index--;
- edit.removed.add(0, edit.e.getElement(edit.index));
- }
+ p++);
+ Edit edit = insertPath[insertPath.length - p - 1];
+ edit.index--;
+ edit.removed.add(0, edit.e.getElement(edit.index));
}
}
@@ -2379,18 +2432,24 @@ public class DefaultStyledDocument extends AbstractDocument implements
if (length == 0)
return;
- UndoableEdit edit = content.insertString(offset,
- contentBuffer.toString());
+ Content c = getContent();
+ UndoableEdit edit = c.insertString(offset,
+ contentBuffer.toString());
// Create the DocumentEvent with the ElementEdit added
DefaultDocumentEvent ev = new DefaultDocumentEvent(offset,
length,
DocumentEvent.EventType.INSERT);
+
ev.addEdit(edit);
// Finally we must update the document structure and fire the insert
// update event.
buffer.insert(offset, length, data, ev);
+
+ super.insertUpdate(ev, null);
+
+ ev.end();
fireInsertUpdate(ev);
fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
}
@@ -2410,14 +2469,16 @@ public class DefaultStyledDocument extends AbstractDocument implements
*/
protected void create(ElementSpec[] data)
{
- writeLock();
try
{
+
// Clear content if there is some.
int len = getLength();
if (len > 0)
remove(0, len);
+ writeLock();
+
// Now we insert the content.
StringBuilder b = new StringBuilder();
for (int i = 0; i < data.length; ++i)
@@ -2429,38 +2490,18 @@ public class DefaultStyledDocument extends AbstractDocument implements
Content content = getContent();
UndoableEdit cEdit = content.insertString(0, b.toString());
+ len = b.length();
DefaultDocumentEvent ev =
new DefaultDocumentEvent(0, b.length(),
DocumentEvent.EventType.INSERT);
ev.addEdit(cEdit);
- // We do a little trick here to get the new structure: We instantiate
- // a new ElementBuffer with a new root element, insert into that root
- // and then reparent the newly created elements to the old root
- // element.
- BranchElement createRoot =
- (BranchElement) createBranchElement(null, null);
- Element dummyLeaf = createLeafElement(createRoot, null, 0, 1);
- createRoot.replace(0, 0, new Element[]{ dummyLeaf });
- ElementBuffer createBuffer = new ElementBuffer(createRoot);
- createBuffer.insert(0, b.length(), data, new DefaultDocumentEvent(0, b.length(), DocumentEvent.EventType.INSERT));
- // Now the new root is the first child of the createRoot.
- Element newRoot = createRoot.getElement(0);
- BranchElement root = (BranchElement) getDefaultRootElement();
- Element[] added = new Element[newRoot.getElementCount()];
- for (int i = 0; i < added.length; ++i)
- {
- added[i] = newRoot.getElement(i);
- ((AbstractElement) added[i]).element_parent = root;
- }
- Element[] removed = new Element[root.getElementCount()];
- for (int i = 0; i < removed.length; ++i)
- removed[i] = root.getElement(i);
+ buffer.create(len, data, ev);
- // Replace the old elements in root with the new and update the event.
- root.replace(0, removed.length, added);
- ev.addEdit(new ElementEdit(root, 0, removed, added));
+ // For the bidi update.
+ super.insertUpdate(ev, null);
+ ev.end();
fireInsertUpdate(ev);
fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
}
diff --git a/javax/swing/text/FlowView.java b/javax/swing/text/FlowView.java
index 9609f3fc8..c2bed399f 100644
--- a/javax/swing/text/FlowView.java
+++ b/javax/swing/text/FlowView.java
@@ -488,7 +488,7 @@ public abstract class FlowView extends BoxView
if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE)
>= ForcedBreakWeight)
{
- max = Math.max(pref, pref);
+ max = Math.max(max, pref);
pref = 0;
}
}
diff --git a/javax/swing/text/GapContent.java b/javax/swing/text/GapContent.java
index 990e9d464..08a318d8b 100644
--- a/javax/swing/text/GapContent.java
+++ b/javax/swing/text/GapContent.java
@@ -39,16 +39,13 @@ exception statement from your version. */
package javax.swing.text;
import java.io.Serializable;
-import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
import java.util.Vector;
-import java.util.WeakHashMap;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
@@ -71,7 +68,7 @@ public class GapContent
/**
* A {@link Position} implementation for <code>GapContent</code>.
*/
- private class GapContentPosition
+ class GapContentPosition
implements Position
{
@@ -82,39 +79,6 @@ public class GapContent
Mark mark;
/**
- * Creates a new GapContentPosition object.
- *
- * @param offset the offset of this Position
- */
- GapContentPosition(int offset)
- {
- // Try to find the mark in the positionMarks array, and store the index
- // to it.
- synchronized (GapContent.this)
- {
- // Try to make space.
- garbageCollect();
- Mark m = new Mark(offset);
- int i = search(marks, m);
- if (i >= 0) // mark found
- {
- m = (Mark) marks.get(i);
- }
- else
- {
- i = -i - 1;
- marks.add(i, m);
- }
- m.refCount++;
- mark = m;
- }
-
- // Register this position in the death queue, so we can cleanup the marks
- // when this position object gets GC'ed.
- new WeakReference(this, queueOfDeath);
- }
-
- /**
* Returns the current offset of this Position within the content.
*
* @return the current offset of this Position within the content.
@@ -133,7 +97,7 @@ public class GapContent
* be garbage collected while we still hold a reference to the Mark object.
*/
private class Mark
- implements Comparable
+ extends WeakReference
{
/**
* The actual mark into the buffer.
@@ -141,21 +105,20 @@ public class GapContent
int mark;
/**
- * The number of GapContentPosition object that reference this mark. If
- * it reaches zero, it get's deleted by {@link GapContent#garbageCollect()}.
- */
- int refCount;
-
- /**
* Creates a new Mark object for the specified offset.
*
* @param offset the offset
*/
Mark(int offset)
{
+ super(null);
+ mark = offset;
+ }
+
+ Mark(int offset, GapContentPosition pos, ReferenceQueue queue)
+ {
+ super(pos, queue);
mark = offset;
- if (mark >= gapStart && mark != 0)
- mark += (gapEnd - gapStart);
}
/**
@@ -165,34 +128,23 @@ public class GapContent
*/
int getOffset()
{
- assert mark == 0 || mark <= gapStart || mark >= gapEnd :
- "Invalid mark: " + mark + ", gapStart: " + gapStart
- + ", gapEnd: " + gapEnd;
-
int res = mark;
- if (mark >= gapEnd)
+ if (mark >= gapStart)
res -= (gapEnd - gapStart);
- return res;
+ return Math.max(0, res);
}
/**
- * Implementation of Comparable.
- */
- public int compareTo(Object o)
- {
- Mark other = (Mark) o;
- return mark - other.mark;
- }
- /**
- * Adjustment for equals().
+ * Returns the GapContentPosition that is associated ith this mark.
+ * This fetches the weakly referenced position object.
+ *
+ * @return the GapContentPosition that is associated ith this mark
*/
- public boolean equals(Object o)
+ GapContentPosition getPosition()
{
- if (o == null || !(o instanceof Mark))
- return false;
- else
- return ((Mark) o).mark == mark;
+ return (GapContentPosition) get();
}
+
}
/**
@@ -367,7 +319,15 @@ public class GapContent
*/
ArrayList marks;
- WeakHashMap positions;
+ /**
+ * The number of unused marks.
+ */
+ private int garbageMarks;
+
+ /**
+ * A 'static' mark that is used for searching.
+ */
+ private Mark searchMark = new Mark(0);
/**
* Queues all references to GapContentPositions that are about to be
@@ -398,7 +358,6 @@ public class GapContent
gapStart = 1;
gapEnd = size;
buffer[0] = '\n';
- positions = new WeakHashMap();
marks = new ArrayList();
queueOfDeath = new ReferenceQueue();
}
@@ -612,27 +571,33 @@ public class GapContent
// and luckily enough the GapContent can very well deal with offsets
// outside the buffer bounds. So I removed that check.
+ // First do some garbage collections.
+ while (queueOfDeath.poll() != null)
+ garbageMarks++;
+ if (garbageMarks > Math.max(5, marks.size() / 10))
+ garbageCollect();
+
// We try to find a GapContentPosition at the specified offset and return
// that. Otherwise we must create a new one.
- GapContentPosition pos = null;
- Set positionSet = positions.keySet();
- for (Iterator i = positionSet.iterator(); i.hasNext();)
- {
- GapContentPosition p = (GapContentPosition) i.next();
- if (p.getOffset() == offset)
- {
- pos = p;
- break;
- }
- }
-
- // If none was found, then create and return a new one.
- if (pos == null)
+ Mark m;
+ GapContentPosition pos;
+ int index = offset;
+ if (offset >= gapStart)
+ index += (gapEnd - gapStart);
+ searchMark.mark = index;
+ int insertIndex = search(searchMark);
+ if (!(insertIndex < marks.size()
+ && (m = (Mark) marks.get(insertIndex)).mark == index
+ && (pos = m.getPosition()) != null))
{
- pos = new GapContentPosition(offset);
- positions.put(pos, null);
+ // Create new position if none was found.
+ pos = new GapContentPosition();
+ m = new Mark(index, pos, queueOfDeath);
+ pos.mark = m;
+ marks.add(insertIndex, m);
}
-
+ // Otherwise use the found position.
+
return pos;
}
@@ -649,18 +614,29 @@ public class GapContent
assert newSize > (gapEnd - gapStart) : "The new gap size must be greater "
+ "than the old gap size";
- int delta = newSize - gapEnd + gapStart;
- // Update the marks after the gapEnd.
- adjustPositionsInRange(gapEnd, -1, delta);
+ int oldEnd = getGapEnd();
+ int oldSize = getArrayLength();
+ int upper = oldSize - oldEnd;
+ int size = (newSize + 1) * 2;
+ int newEnd = size - upper;
// Copy the data around.
- char[] newBuf = (char[]) allocateArray(length() + newSize);
- System.arraycopy(buffer, 0, newBuf, 0, gapStart);
- System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, buffer.length
- - gapEnd);
- gapEnd = gapStart + newSize;
+ char[] newBuf = (char[]) allocateArray(size);
+ System.arraycopy(buffer, 0, newBuf, 0, Math.min(size, oldSize));
buffer = newBuf;
-
+ gapEnd = newEnd;
+ if (upper != 0)
+ System.arraycopy(buffer, oldEnd, buffer, newEnd, upper);
+
+ // Adjust marks.
+ int delta = gapEnd - oldEnd;
+ int adjIndex = searchFirst(oldEnd);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ m.mark += delta;
+ }
}
/**
@@ -670,28 +646,44 @@ public class GapContent
*/
protected void shiftGap(int newGapStart)
{
- if (newGapStart == gapStart)
- return;
- int newGapEnd = newGapStart + gapEnd - gapStart;
- if (newGapStart < gapStart)
+ int oldStart = gapStart;
+ int delta = newGapStart - oldStart;
+ int oldEnd = gapEnd;
+ int newGapEnd = oldEnd + delta;
+ int size = oldEnd - oldStart;
+
+ // Shift gap in array.
+ gapStart = newGapStart;
+ gapEnd = newGapEnd;
+ if (delta > 0)
+ System.arraycopy(buffer, oldEnd, buffer, oldStart, delta);
+ else
+ System.arraycopy(buffer, newGapStart, buffer, newGapEnd, -delta);
+
+ // Adjust marks.
+ if (delta > 0)
{
- // Update the positions between newGapStart and (old) gapStart. The marks
- // must be shifted by (gapEnd - gapStart).
- adjustPositionsInRange(newGapStart, gapStart, gapEnd - gapStart);
- System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart
- - newGapStart);
- gapStart = newGapStart;
- gapEnd = newGapEnd;
+ int adjIndex = searchFirst(oldStart);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark >= newGapEnd)
+ break;
+ m.mark -= size;
+ }
}
- else
+ else if (delta < 0)
{
- // Update the positions between newGapEnd and (old) gapEnd. The marks
- // must be shifted by (gapEnd - gapStart).
- adjustPositionsInRange(gapEnd, newGapEnd, -(gapEnd - gapStart));
- System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart
- - gapStart);
- gapStart = newGapStart;
- gapEnd = newGapEnd;
+ int adjIndex = searchFirst(newGapStart);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark >= oldEnd)
+ break;
+ m.mark += size;
+ }
}
resetMarksAtZero();
}
@@ -711,7 +703,18 @@ public class GapContent
assert newGapStart < gapStart : "The new gap start must be less than the "
+ "old gap start.";
- setPositionsInRange(newGapStart, gapStart, false);
+
+ // Adjust positions.
+ int adjIndex = searchFirst(newGapStart);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark > gapStart)
+ break;
+ m.mark = gapEnd;
+ }
+
gapStart = newGapStart;
resetMarksAtZero();
}
@@ -731,7 +734,19 @@ public class GapContent
assert newGapEnd > gapEnd : "The new gap end must be greater than the "
+ "old gap end.";
- setPositionsInRange(gapEnd, newGapEnd, false);
+
+ // Adjust marks.
+ int adjIndex = searchFirst(gapEnd);
+ int count = marks.size();
+ for (int i = adjIndex; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.mark >= newGapEnd)
+ break;
+ m.mark = newGapEnd;
+ }
+
+
gapEnd = newGapEnd;
resetMarksAtZero();
}
@@ -757,23 +772,88 @@ public class GapContent
protected void replace(int position, int rmSize, Object addItems,
int addSize)
{
- if (gapStart != position)
- shiftGap(position);
-
- // Remove content
- if (rmSize > 0)
- shiftGapEndUp(gapEnd + rmSize);
+ if (addSize == 0)
+ {
+ removeImpl(position, rmSize);
+ return;
+ }
+ else if (rmSize > addSize)
+ {
+ removeImpl(position + addSize, rmSize - addSize);
+ }
+ else
+ {
+ int endSize = addSize - rmSize;
+ int end = addImpl(position + rmSize, endSize);
+ System.arraycopy(addItems, rmSize, buffer, end, endSize);
+ addSize = rmSize;
+ }
+ System.arraycopy(addItems, 0, buffer, position, addSize);
+ }
+
+ /**
+ * Adjusts the positions and gap in response to a remove operation.
+ *
+ * @param pos the position at which to remove
+ * @param num the number of removed items
+ */
+ private void removeImpl(int pos, int num)
+ {
+ if (num > 0)
+ {
+ int end = pos + num;
+ int newGapSize = (gapEnd - gapStart) + num;
+ if (end <= gapStart)
+ {
+ if (gapStart != end)
+ {
+ shiftGap(end);
+ }
+ shiftGapStartDown(gapStart - num);
+ }
+ else if (pos >= gapStart)
+ {
+ if (gapStart != pos)
+ {
+ shiftGap(pos);
+ }
+ shiftGapEndUp(gapStart + newGapSize);
+ }
+ else
+ {
+ shiftGapStartDown(pos);
+ shiftGapEndUp(gapStart + newGapSize);
+ }
+ }
+ }
- // If gap is too small, enlarge the gap.
- if ((gapEnd - gapStart) <= addSize)
- shiftEnd((addSize - gapEnd + gapStart + 1) * 2 + gapEnd + DEFAULT_BUFSIZE);
+ /**
+ * Adjusts the positions and gap in response to an add operation.
+ *
+ * @param pos the position at which to add
+ * @param num the number of added items
+ *
+ * @return the adjusted position
+ */
+ private int addImpl(int pos, int num)
+ {
+ int size = gapEnd - gapStart;
+ if (num == 0)
+ {
+ if (pos > gapStart)
+ pos += size;
+ return pos;
+ }
- // Add new items to the buffer.
- if (addItems != null)
+ shiftGap(pos);
+ if (num >= size)
{
- System.arraycopy(addItems, 0, buffer, gapStart, addSize);
- gapStart += addSize;
+ shiftEnd(getArrayLength() - size + num);
+ size = gapEnd - gapStart;
}
+
+ gapStart += num;
+ return pos;
}
/**
@@ -808,95 +888,34 @@ public class GapContent
*/
protected Vector getPositionsInRange(Vector v, int offset, int length)
{
- Vector res = v;
- if (res == null)
- res = new Vector();
-
- int endOffs = offset + length;
-
- Set positionSet = positions.keySet();
- for (Iterator i = positionSet.iterator(); i.hasNext();)
+ int end = offset + length;
+ int startIndex;
+ int endIndex;
+ if (offset < gapStart)
{
- GapContentPosition p = (GapContentPosition) i.next();
- int offs = p.getOffset();
- if (offs >= offset && offs <= endOffs)
- res.add(new UndoPosRef(p.mark));
+ if (offset == 0)
+ startIndex = 0;
+ else
+ startIndex = searchFirst(offset);
+ if (end >= gapStart)
+ endIndex = searchFirst(end + (gapEnd - gapStart) + 1);
+ else
+ endIndex = searchFirst(end + 1);
}
-
- return res;
- }
-
- /**
- * Crunches all positions in the specified range to either the start or
- * end of that interval. The interval boundaries are meant to be inclusive
- * [start, end].
- *
- * @param start the start offset of the range
- * @param end the end offset of the range
- * @param toStart a boolean indicating if the positions should be crunched
- * to the start (true) or to the end (false)
- */
- private void setPositionsInRange(int start, int end, boolean toStart)
- {
- synchronized (this)
+ else
{
- // Find the start and end indices in the positionMarks array.
- Mark m = new Mark(0); // For comparison / search only.
- m.mark = start;
- int startIndex = search(marks, m);
- if (startIndex < 0) // Translate to insertion index, if not found.
- startIndex = - startIndex - 1;
- m.mark = end;
- int endIndex = search(marks, m);
- if (endIndex < 0) // Translate to insertion index - 1, if not found.
- endIndex = - endIndex - 2;
-
- // Actually adjust the marks.
- for (int i = startIndex; i <= endIndex; i++)
- ((Mark) marks.get(i)).mark = toStart ? start : end;
+ startIndex = searchFirst(offset + (gapEnd - gapStart));
+ endIndex = searchFirst(end + (gapEnd - gapStart) + 1);
}
-
- }
-
- /**
- * Adjusts the mark of all <code>Position</code>s that are in the range
- * specified by <code>offset</code> and </code>length</code> within
- * the buffer array by <code>increment</code>
- *
- * @param startOffs the start offset of the range to search
- * @param endOffs the length of the range to search, -1 means all to the end
- * @param incr the increment
- */
- private void adjustPositionsInRange(int startOffs, int endOffs, int incr)
- {
- synchronized (this)
+ if (v == null)
+ v = new Vector();
+ for (int i = startIndex; i < endIndex; i++)
{
- // Find the start and end indices in the positionMarks array.
- Mark m = new Mark(0); // For comparison / search only.
-
- m.mark = startOffs;
- int startIndex = search(marks, m);
- if (startIndex < 0) // Translate to insertion index, if not found.
- startIndex = - startIndex - 1;
-
- m.mark = endOffs;
- int endIndex;
- if (endOffs == -1)
- endIndex = marks.size() - 1;
- else
- {
- endIndex = search(marks, m);
- if (endIndex < 0) // Translate to insertion index - 1, if not found.
- endIndex = - endIndex - 2;
- }
- // Actually adjust the marks.
- for (int i = startIndex; i <= endIndex; i++) {
- ((Mark) marks.get(i)).mark += incr;
- }
+ v.add(new UndoPosRef((Mark) marks.get(i)));
}
-
+ return v;
}
-
+
/**
* Resets all <code>Position</code> that have an offset of <code>0</code>,
* to also have an array index of <code>0</code>. This might be necessary
@@ -977,30 +996,6 @@ public class GapContent
}
/**
- * Polls the queue of death for GapContentPositions, updates the
- * corresponding reference count and removes the corresponding mark
- * if the refcount reaches zero.
- *
- * This is package private to avoid accessor synthetic methods.
- */
- void garbageCollect()
- {
- Reference ref = queueOfDeath.poll();
- while (ref != null)
- {
- if (ref != null)
- {
- GapContentPosition pos = (GapContentPosition) ref.get();
- Mark m = pos.mark;
- m.refCount--;
- if (m.refCount == 0)
- marks.remove(m);
- }
- ref = queueOfDeath.poll();
- }
- }
-
- /**
* Searches the first occurance of object <code>o</code> in list
* <code>l</code>. This performs a binary search by calling
* {@link Collections#binarySearch(List, Object)} and when an object has been
@@ -1008,22 +1003,93 @@ public class GapContent
* list. The meaning of the return value is the same as in
* <code>Collections.binarySearch()</code>.
*
- * @param l the list to search through
* @param o the object to be searched
*
* @return the index of the first occurance of o in l, or -i + 1 if not found
*/
- int search(List l, Object o)
+ int search(Mark o)
{
- int i = Collections.binarySearch(l, o);
- while (i > 0)
+ int foundInd = 0;
+ boolean found = false;
+ int low = 0;
+ int up = marks.size() - 1;
+ int mid = 0;
+ if (up > -1)
{
- Object o2 = l.get(i - 1);
- if (o2.equals(o))
- i--;
+ int cmp = 0;
+ Mark last = (Mark) marks.get(up);
+ cmp = compare(o, last);
+ if (cmp > 0)
+ {
+ foundInd = up + 1;
+ found = true;
+ }
else
+ {
+ while (low <= up && ! found)
+ {
+ mid = low + (up - low) / 2;
+ Mark m = (Mark) marks.get(mid);
+ cmp = compare(o, m);
+ if (cmp == 0)
+ {
+ foundInd = mid;
+ found = true;
+ }
+ else if (cmp < 0)
+ up = mid - 1;
+ else
+ low = mid + 1;
+ }
+
+ if (! found)
+ foundInd = cmp < 0 ? mid : mid + 1;
+ }
+ }
+ return foundInd;
+ }
+
+ private int searchFirst(int index)
+ {
+ searchMark.mark = Math.max(index, 1);
+ int i = search(searchMark);
+ for (int j = i - 1; j >= 0; j--)
+ {
+ Mark m = (Mark) marks.get(j);
+ if (m.mark != index)
break;
+ i--;
}
return i;
}
+
+ /**
+ * Compares two marks.
+ *
+ * @param m1 the first mark
+ * @param m2 the second mark
+ *
+ * @return negative when m1 < m2, positive when m1 > m2 and 0 when equal
+ */
+ private int compare(Mark m1, Mark m2)
+ {
+ return m1.mark - m2.mark;
+ }
+
+ /**
+ * Collects and frees unused marks.
+ */
+ private void garbageCollect()
+ {
+ int count = marks.size();
+ ArrayList clean = new ArrayList();
+ for (int i = 0; i < count; i++)
+ {
+ Mark m = (Mark) marks.get(i);
+ if (m.get() != null)
+ clean.add(m);
+ }
+ marks = clean;
+ garbageMarks = 0;
+ }
}
diff --git a/javax/swing/text/GlyphView.java b/javax/swing/text/GlyphView.java
index 35c8dd5d7..d5070a6a9 100644
--- a/javax/swing/text/GlyphView.java
+++ b/javax/swing/text/GlyphView.java
@@ -278,44 +278,27 @@ public class GlyphView extends View implements TabableView, Cloneable
public void paint(GlyphView view, Graphics g, Shape a, int p0,
int p1)
{
- Color oldColor = g.getColor();
- int height = (int) getHeight(view);
+ updateFontMetrics(view);
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ TabExpander tabEx = view.getTabExpander();
Segment txt = view.getText(p0, p1);
- Rectangle bounds = a.getBounds();
- TabExpander tabEx = null;
- View parent = view.getParent();
- if (parent instanceof TabExpander)
- tabEx = (TabExpander) parent;
-
- int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(),
- bounds.x, tabEx, txt.offset);
- // Fill the background of the text run.
- Color background = view.getBackground();
- if (background != null)
- {
- g.setColor(background);
- g.fillRect(bounds.x, bounds.y, width, height);
- }
- // Draw the actual text.
- g.setColor(view.getForeground());
- g.setFont(view.getFont());
- int ascent = g.getFontMetrics().getAscent();
- Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent, g, tabEx,
- txt.offset);
-
- if (view.isStrikeThrough())
- {
- int strikeHeight = (int) (getAscent(view) / 2);
- g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.x + width,
- bounds.y + strikeHeight);
- }
- if (view.isUnderline())
+
+ // Find out the X location at which we have to paint.
+ int x = r.x;
+ int p = view.getStartOffset();
+ if (p != p0)
{
- int lineHeight = (int) getAscent(view);
- g.drawLine(bounds.x, bounds.y + lineHeight, bounds.x + width,
- bounds.y + lineHeight);
+ int width = Utilities.getTabbedTextWidth(txt, fontMetrics,x, tabEx,
+ p);
+ x += width;
}
- g.setColor(oldColor);
+ // Find out Y location.
+ int y = r.y + fontMetrics.getHeight() - fontMetrics.getDescent();
+
+ // Render the thing.
+ g.setFont(fontMetrics.getFont());
+ Utilities.drawTabbedText(txt, x, y, g, tabEx, p0);
+
}
/**
@@ -497,6 +480,16 @@ public class GlyphView extends View implements TabableView, Cloneable
private int length;
/**
+ * The x location against which the tab expansion is done.
+ */
+ private float tabX;
+
+ /**
+ * The tab expander that is used in this view.
+ */
+ private TabExpander tabExpander;
+
+ /**
* Creates a new <code>GlyphView</code> for the given <code>Element</code>.
*
* @param element the element that is rendered by this GlyphView
@@ -555,11 +548,29 @@ public class GlyphView extends View implements TabableView, Cloneable
int p0 = getStartOffset();
int p1 = getEndOffset();
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
Container c = getContainer();
- // Paint layered highlights if there are any.
+
+ Color fg = getForeground();
+ JTextComponent tc = null;
if (c instanceof JTextComponent)
{
- JTextComponent tc = (JTextComponent) c;
+ tc = (JTextComponent) c;
+ if (! tc.isEnabled())
+ fg = tc.getDisabledTextColor();
+ }
+ Color bg = getBackground();
+ if (bg != null)
+ {
+ g.setColor(bg);
+ System.err.println("fill background: " + bg);
+ g.fillRect(r.x, r.y, r.width, r.height);
+ }
+
+
+ // Paint layered highlights if there are any.
+ if (tc != null)
+ {
Highlighter h = tc.getHighlighter();
if (h instanceof LayeredHighlighter)
{
@@ -568,7 +579,45 @@ public class GlyphView extends View implements TabableView, Cloneable
}
}
- getGlyphPainter().paint(this, g, a, p0, p1);
+ g.setColor(fg);
+ glyphPainter.paint(this, g, a, p0, p1);
+ boolean underline = isUnderline();
+ boolean striked = isStrikeThrough();
+ if (underline || striked)
+ {
+ View parent = getParent();
+ // X coordinate.
+ if (parent != null && parent.getEndOffset() == p1)
+ {
+ // Strip whitespace.
+ Segment s = getText(p0, p1);
+ while (s.count > 0 && Character.isWhitespace(s.array[s.count - 1]))
+ {
+ p1--;
+ s.count--;
+ }
+ }
+ int x0 = r.x;
+ int p = getStartOffset();
+ TabExpander tabEx = getTabExpander();
+ if (p != p0)
+ x0 += (int) glyphPainter.getSpan(this, p, p0, tabEx, x0);
+ int x1 = x0 + (int) glyphPainter.getSpan(this, p0, p1, tabEx, x0);
+ // Y coordinate.
+ int y = r.y + r.height - (int) glyphPainter.getDescent(this);
+ if (underline)
+ {
+ int yTmp = y;
+ yTmp += 1;
+ g.drawLine(x0, yTmp, x1, yTmp);
+ }
+ if (striked)
+ {
+ int yTmp = y;
+ yTmp -= (int) glyphPainter.getAscent(this);
+ g.drawLine(x0, yTmp, x1, yTmp);
+ }
+ }
}
@@ -658,13 +707,7 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public TabExpander getTabExpander()
{
- TabExpander te = null;
- View parent = getParent();
-
- if (parent instanceof TabExpander)
- te = (TabExpander) parent;
-
- return te;
+ return tabExpander;
}
/**
@@ -678,8 +721,16 @@ public class GlyphView extends View implements TabableView, Cloneable
public float getTabbedSpan(float x, TabExpander te)
{
checkPainter();
+ TabExpander old = tabExpander;
+ tabExpander = te;
+ if (tabExpander != old)
+ {
+ // Changing the tab expander will lead to a relayout in the X_AXIS.
+ preferenceChanged(null, true, false);
+ }
+ tabX = x;
return getGlyphPainter().getSpan(this, getStartOffset(),
- getEndOffset(), te, x);
+ getEndOffset(), tabExpander, x);
}
/**
@@ -693,23 +744,8 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public float getPartialSpan(int p0, int p1)
{
- Element el = getElement();
- Document doc = el.getDocument();
- Segment seg = new Segment();
- try
- {
- doc.getText(p0, p1 - p0, seg);
- }
- catch (BadLocationException ex)
- {
- AssertionError ae;
- ae = new AssertionError("BadLocationException must not be thrown "
- + "here");
- ae.initCause(ex);
- throw ae;
- }
- FontMetrics fm = null; // Fetch font metrics somewhere.
- return Utilities.getTabbedTextWidth(seg, fm, 0, null, p0);
+ checkPainter();
+ return glyphPainter.getSpan(this, p0, p1, tabExpander, tabX);
}
/**
@@ -746,6 +782,8 @@ public class GlyphView extends View implements TabableView, Cloneable
return offs;
}
+ private Segment cached = new Segment();
+
/**
* Returns the text segment that this view is responsible for.
*
@@ -756,10 +794,9 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public Segment getText(int p0, int p1)
{
- Segment txt = new Segment();
try
{
- getDocument().getText(p0, p1 - p0, txt);
+ getDocument().getText(p0, p1 - p0, cached);
}
catch (BadLocationException ex)
{
@@ -770,7 +807,7 @@ public class GlyphView extends View implements TabableView, Cloneable
throw ae;
}
- return txt;
+ return cached;
}
/**
@@ -938,6 +975,8 @@ public class GlyphView extends View implements TabableView, Cloneable
if (p0 != getStartOffset() || end != getEndOffset())
{
brokenView = createFragment(p0, end);
+ if (brokenView instanceof GlyphView)
+ ((GlyphView) brokenView).tabX = pos;
}
}
return brokenView;
@@ -1007,7 +1046,7 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf)
{
- preferenceChanged(this, true, true);
+ preferenceChanged(null, true, true);
}
/**
@@ -1022,7 +1061,7 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
{
- preferenceChanged(this, true, false);
+ preferenceChanged(null, true, false);
}
/**
@@ -1037,7 +1076,7 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf)
{
- preferenceChanged(this, true, false);
+ preferenceChanged(null, true, false);
}
/**
diff --git a/javax/swing/text/StyleContext.java b/javax/swing/text/StyleContext.java
index b01d1060f..4dded0d04 100644
--- a/javax/swing/text/StyleContext.java
+++ b/javax/swing/text/StyleContext.java
@@ -48,10 +48,12 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.ref.WeakReference;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Hashtable;
import java.util.Iterator;
+import java.util.Map;
import java.util.WeakHashMap;
import javax.swing.event.ChangeEvent;
@@ -467,7 +469,8 @@ public class StyleContext
/**
* A pool of immutable AttributeSets.
*/
- private transient WeakHashMap attributeSetPool = new WeakHashMap();
+ private transient Map attributeSetPool =
+ Collections.synchronizedMap(new WeakHashMap());
/**
* Creates a new instance of the style context. Add the default style
@@ -545,7 +548,7 @@ public class StyleContext
throws ClassNotFoundException, IOException
{
search = new SimpleAttributeSet();
- attributeSetPool = new WeakHashMap();
+ attributeSetPool = Collections.synchronizedMap(new WeakHashMap());
in.defaultReadObject();
}
@@ -650,7 +653,8 @@ public class StyleContext
return defaultStyleContext;
}
- public AttributeSet addAttribute(AttributeSet old, Object name, Object value)
+ public synchronized AttributeSet addAttribute(AttributeSet old, Object name,
+ Object value)
{
AttributeSet ret;
if (old.getAttributeCount() + 1 < getCompressionThreshold())
@@ -670,7 +674,8 @@ public class StyleContext
return ret;
}
- public AttributeSet addAttributes(AttributeSet old, AttributeSet attributes)
+ public synchronized AttributeSet addAttributes(AttributeSet old,
+ AttributeSet attributes)
{
AttributeSet ret;
if (old.getAttributeCount() + attributes.getAttributeCount()
@@ -701,7 +706,8 @@ public class StyleContext
cleanupPool();
}
- public AttributeSet removeAttribute(AttributeSet old, Object name)
+ public synchronized AttributeSet removeAttribute(AttributeSet old,
+ Object name)
{
AttributeSet ret;
if (old.getAttributeCount() - 1 <= getCompressionThreshold())
@@ -721,7 +727,8 @@ public class StyleContext
return ret;
}
- public AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes)
+ public synchronized AttributeSet removeAttributes(AttributeSet old,
+ AttributeSet attributes)
{
AttributeSet ret;
if (old.getAttributeCount() <= getCompressionThreshold())
@@ -741,7 +748,8 @@ public class StyleContext
return ret;
}
- public AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names)
+ public synchronized AttributeSet removeAttributes(AttributeSet old,
+ Enumeration<?> names)
{
AttributeSet ret;
if (old.getAttributeCount() <= getCompressionThreshold())
diff --git a/javax/swing/text/Utilities.java b/javax/swing/text/Utilities.java
index fa2d1ab52..d49d806cf 100644
--- a/javax/swing/text/Utilities.java
+++ b/javax/swing/text/Utilities.java
@@ -89,12 +89,12 @@ public class Utilities
// The font metrics of the current selected font.
FontMetrics metrics = g.getFontMetrics();
+
int ascent = metrics.getAscent();
// The current x and y pixel coordinates.
int pixelX = x;
- int pixelWidth = 0;
int pos = s.offset;
int len = 0;
@@ -103,39 +103,43 @@ public class Utilities
for (int offset = s.offset; offset < end; ++offset)
{
char c = buffer[offset];
- if (c == '\t')
+ switch (c)
{
+ case '\t':
if (len > 0) {
g.drawChars(buffer, pos, len, pixelX, y);
- pixelX += pixelWidth;
- pixelWidth = 0;
+ pixelX += metrics.charsWidth(buffer, pos, len);
+ len = 0;
}
pos = offset+1;
- len = 0;
+ if (e != null)
+ pixelX = (int) e.nextTabStop((float) pixelX, startOffset + offset
+ - s.offset);
+ else
+ pixelX += metrics.charWidth(' ');
+ x = pixelX;
+ break;
+ case '\n':
+ case '\r':
+ if (len > 0) {
+ g.drawChars(buffer, pos, len, pixelX, y);
+ pixelX += metrics.charsWidth(buffer, pos, len);
+ len = 0;
+ }
+ x = pixelX;
+ break;
+ default:
+ len += 1;
}
-
- switch (c)
- {
- case '\t':
- // In case we have a tab, we just 'jump' over the tab.
- // When we have no tab expander we just use the width of ' '.
- if (e != null)
- pixelX = (int) e.nextTabStop(pixelX,
- startOffset + offset - s.offset);
- else
- pixelX += metrics.charWidth(' ');
- break;
- default:
- ++len;
- pixelWidth += metrics.charWidth(buffer[offset]);
- break;
- }
}
if (len > 0)
- g.drawChars(buffer, pos, len, pixelX, y);
+ {
+ g.drawChars(buffer, pos, len, pixelX, y);
+ pixelX += metrics.charsWidth(buffer, pos, len);
+ }
- return pixelX + pixelWidth;
+ return pixelX;
}
/**
@@ -163,7 +167,9 @@ public class Utilities
// The current maximum width.
int maxWidth = 0;
- for (int offset = s.offset; offset < (s.offset + s.count); ++offset)
+ int end = s.offset + s.count;
+ int count = 0;
+ for (int offset = s.offset; offset < end; offset++)
{
switch (buffer[offset])
{
@@ -179,21 +185,18 @@ public class Utilities
case '\n':
// In case we have a newline, we must 'draw'
// the buffer and jump on the next line.
- pixelX += metrics.charWidth(buffer[offset]);
- maxWidth = Math.max(maxWidth, pixelX - x);
- pixelX = x;
- break;
- default:
- // Here we draw the char.
- pixelX += metrics.charWidth(buffer[offset]);
- break;
- }
+ pixelX += metrics.charsWidth(buffer, offset - count, count);
+ count = 0;
+ break;
+ default:
+ count++;
+ }
}
// Take the last line into account.
- maxWidth = Math.max(maxWidth, pixelX - x);
+ pixelX += metrics.charsWidth(buffer, end - count, count);
- return maxWidth;
+ return pixelX - x;
}
/**
@@ -228,43 +231,41 @@ public class Utilities
int x, TabExpander te, int p0,
boolean round)
{
- // At the end of the for loop, this holds the requested model location
- int pos;
+ int found = s.count;
int currentX = x0;
- int width = 0;
+ int nextX = currentX;
- for (pos = 0; pos < s.count; pos++)
+ int end = s.offset + s.count;
+ for (int pos = s.offset; pos < end && found == s.count; pos++)
{
- char nextChar = s.array[s.offset+pos];
-
- if (nextChar == 0)
- break;
+ char nextChar = s.array[pos];
if (nextChar != '\t')
- width = fm.charWidth(nextChar);
+ nextX += fm.charWidth(nextChar);
else
{
if (te == null)
- width = fm.charWidth(' ');
+ nextX += fm.charWidth(' ');
else
- width = ((int) te.nextTabStop(currentX, pos)) - currentX;
+ nextX += ((int) te.nextTabStop(nextX, p0 + pos - s.offset));
}
- if (round)
+ if (x >= currentX && x < nextX)
{
- if (currentX + (width>>1) > x)
- break;
- }
- else
- {
- if (currentX + width > x)
- break;
+ // Found position.
+ if ((! round) || ((x - currentX) < (nextX - x)))
+ {
+ found = pos - s.offset;
+ }
+ else
+ {
+ found = pos + 1 - s.offset;
+ }
}
-
- currentX += width;
+ currentX = nextX;
}
- return pos;
+ return found;
}
/**
diff --git a/javax/swing/text/html/BlockView.java b/javax/swing/text/html/BlockView.java
index d7519ef9a..2e781412c 100644
--- a/javax/swing/text/html/BlockView.java
+++ b/javax/swing/text/html/BlockView.java
@@ -171,6 +171,22 @@ public class BlockView extends BoxView
}
else
r = super.calculateMinorAxisRequirements(axis, r);
+
+ // Apply text alignment if appropriate.
+ if (axis == X_AXIS)
+ {
+ Object o = getAttributes().getAttribute(CSS.Attribute.TEXT_ALIGN);
+ if (o != null)
+ {
+ String al = o.toString().trim();
+ if (al.equals("center"))
+ r.alignment = 0.5f;
+ else if (al.equals("right"))
+ r.alignment = 1.0f;
+ else
+ r.alignment = 0.0f;
+ }
+ }
return r;
}
diff --git a/javax/swing/text/html/CSS.java b/javax/swing/text/html/CSS.java
index 6461dca9a..c82b6c537 100644
--- a/javax/swing/text/html/CSS.java
+++ b/javax/swing/text/html/CSS.java
@@ -415,6 +415,8 @@ public class CSS implements Serializable
new Attribute("border-left-color", false, null);
static final Attribute BORDER_RIGHT_COLOR =
new Attribute("border-right-color", false, null);
+ static final Attribute BORDER_SPACING =
+ new Attribute("border-spacing", false, null);
/**
* The attribute string.
@@ -516,7 +518,10 @@ public class CSS implements Serializable
else if (att == Attribute.MARGIN || att == Attribute.MARGIN_BOTTOM
|| att == Attribute.MARGIN_LEFT || att == Attribute.MARGIN_RIGHT
|| att == Attribute.MARGIN_TOP || att == Attribute.WIDTH
- || att == Attribute.HEIGHT)
+ || att == Attribute.HEIGHT
+ || att == Attribute.PADDING || att == Attribute.PADDING_BOTTOM
+ || att == Attribute.PADDING_LEFT || att == Attribute.PADDING_RIGHT
+ || att == Attribute.PADDING_TOP)
o = new Length(v);
else if (att == Attribute.BORDER_WIDTH || att == Attribute.BORDER_TOP_WIDTH
|| att == Attribute.BORDER_LEFT_WIDTH
@@ -543,7 +548,7 @@ public class CSS implements Serializable
String token = tokens.nextToken();
if (CSSColor.isValidColor(token))
atts.addAttribute(Attribute.BACKGROUND_COLOR,
- getValue(Attribute.BACKGROUND_COLOR, token));
+ new CSSColor(token));
}
}
}
diff --git a/javax/swing/text/html/HTMLDocument.java b/javax/swing/text/html/HTMLDocument.java
index 26e3fb4bc..ee59d7025 100644
--- a/javax/swing/text/html/HTMLDocument.java
+++ b/javax/swing/text/html/HTMLDocument.java
@@ -184,8 +184,6 @@ public class HTMLDocument extends DefaultStyledDocument
protected Element createLeafElement(Element parent, AttributeSet a, int p0,
int p1)
{
- RunElement el = new RunElement(parent, a, p0, p1);
- el.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
return new RunElement(parent, a, p0, p1);
}
@@ -454,6 +452,8 @@ public class HTMLDocument extends DefaultStyledDocument
String name = null;
if (tag != null)
name = tag.toString();
+ if (name == null)
+ name = super.getName();
return name;
}
}
@@ -490,6 +490,8 @@ public class HTMLDocument extends DefaultStyledDocument
String name = null;
if (tag != null)
name = tag.toString();
+ if (name == null)
+ name = super.getName();
return name;
}
@@ -511,7 +513,17 @@ public class HTMLDocument extends DefaultStyledDocument
* @author Anthony Balkissoon abalkiss at redhat dot com
*/
public class HTMLReader extends HTMLEditorKit.ParserCallback
- {
+ {
+ /**
+ * The maximum token threshold. We don't grow it larger than this.
+ */
+ private static final int MAX_THRESHOLD = 10000;
+
+ /**
+ * The threshold growth factor.
+ */
+ private static final int GROW_THRESHOLD = 5;
+
/**
* Holds the current character attribute set *
*/
@@ -523,12 +535,6 @@ public class HTMLDocument extends DefaultStyledDocument
* A stack for character attribute sets *
*/
Stack charAttrStack = new Stack();
-
- /**
- * The parse stack. This stack holds HTML.Tag objects that reflect the
- * current position in the parsing process.
- */
- Stack parseStack = new Stack();
/** A mapping between HTML.Tag objects and the actions that handle them **/
HashMap tagToAction;
@@ -610,6 +616,11 @@ public class HTMLDocument extends DefaultStyledDocument
*/
Document textAreaDocument;
+ /**
+ * The token threshold. This gets increased while loading.
+ */
+ private int threshold;
+
public class TagAction
{
/**
@@ -816,7 +827,7 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public void start(HTML.Tag t, MutableAttributeSet a)
{
- blockOpen(t, a);
+ super.start(t, a);
inParagraph = true;
}
@@ -826,7 +837,7 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public void end(HTML.Tag t)
{
- blockClose(t);
+ super.end(t);
inParagraph = false;
}
}
@@ -1162,6 +1173,7 @@ public class HTMLDocument extends DefaultStyledDocument
this.offset = offset;
this.popDepth = popDepth;
this.pushDepth = pushDepth;
+ threshold = getTokenThreshold();
initTags();
}
@@ -1299,18 +1311,28 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public void flush() throws BadLocationException
{
- DefaultStyledDocument.ElementSpec[] elements;
- elements = new DefaultStyledDocument.ElementSpec[parseBuffer.size()];
- parseBuffer.copyInto(elements);
- parseBuffer.removeAllElements();
- if (offset == 0)
- create(elements);
- else
- insert(offset, elements);
+ flushImpl();
+ }
- offset += HTMLDocument.this.getLength() - offset;
+ /**
+ * Flushes the buffer and handle partial inserts.
+ *
+ */
+ private void flushImpl()
+ throws BadLocationException
+ {
+ int oldLen = getLength();
+ int size = parseBuffer.size();
+ ElementSpec[] elems = new ElementSpec[size];
+ parseBuffer.copyInto(elems);
+ if (oldLen == 0)
+ create(elems);
+ else
+ insert(offset, elems);
+ parseBuffer.removeAllElements();
+ offset += getLength() - oldLen;
}
-
+
/**
* This method is called by the parser to indicate a block of
* text was encountered. Should insert the text appropriately.
@@ -1512,7 +1534,6 @@ public class HTMLDocument extends DefaultStyledDocument
DefaultStyledDocument.ElementSpec element;
- parseStack.push(t);
AbstractDocument.AttributeContext ctx = getAttributeContext();
AttributeSet copy = attr.copyAttributes();
copy = ctx.addAttribute(copy, StyleConstants.NameAttribute, t);
@@ -1542,25 +1563,17 @@ public class HTMLDocument extends DefaultStyledDocument
// If the previous tag is a start tag then we insert a synthetic
// content tag.
DefaultStyledDocument.ElementSpec prev;
- prev = (DefaultStyledDocument.ElementSpec)
- parseBuffer.get(parseBuffer.size() - 1);
- if (prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType)
+ prev = parseBuffer.size() > 0 ? (DefaultStyledDocument.ElementSpec)
+ parseBuffer.get(parseBuffer.size() - 1) : null;
+ if (prev != null &&
+ prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType)
{
- AbstractDocument.AttributeContext ctx = getAttributeContext();
- AttributeSet attributes = ctx.getEmptySet();
- attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute,
- HTML.Tag.CONTENT);
- element = new DefaultStyledDocument.ElementSpec(attributes,
- DefaultStyledDocument.ElementSpec.ContentType,
- new char[0], 0, 0);
- parseBuffer.add(element);
+ addContent(new char[]{' '}, 0, 1);
}
element = new DefaultStyledDocument.ElementSpec(null,
DefaultStyledDocument.ElementSpec.EndTagType);
parseBuffer.addElement(element);
- if (parseStack.size() > 0)
- parseStack.pop();
}
/**
@@ -1615,11 +1628,13 @@ public class HTMLDocument extends DefaultStyledDocument
// Add the element to the buffer
parseBuffer.addElement(element);
- if (parseBuffer.size() > HTMLDocument.this.getTokenThreshold())
+ if (parseBuffer.size() > threshold)
{
+ if (threshold <= MAX_THRESHOLD)
+ threshold *= GROW_THRESHOLD;
try
{
- flush();
+ flushImpl();
}
catch (BadLocationException ble)
{
@@ -1734,10 +1749,6 @@ public class HTMLDocument extends DefaultStyledDocument
}
};
- // Set the parent HTML tag.
- reader.parseStack.push(parent.getAttributes().getAttribute(
- StyleConstants.NameAttribute));
-
return reader;
}
diff --git a/javax/swing/text/html/HTMLEditorKit.java b/javax/swing/text/html/HTMLEditorKit.java
index 85d5221d3..f3a3d90b6 100644
--- a/javax/swing/text/html/HTMLEditorKit.java
+++ b/javax/swing/text/html/HTMLEditorKit.java
@@ -39,8 +39,6 @@ exception statement from your version. */
package javax.swing.text.html;
-import gnu.classpath.NotImplementedException;
-
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
@@ -290,7 +288,7 @@ public class HTMLEditorKit
* Tag to check for in the document.
*/
protected HTML.Tag parentTag;
-
+
/**
* Initializes all fields.
*
@@ -394,20 +392,9 @@ public class HTMLEditorKit
Element insertElement,
String html, HTML.Tag parentTag,
HTML.Tag addTag)
- throws NotImplementedException
{
- /*
- As its name implies, this protected method is used when HTML is inserted at a
- boundary. (A boundary in this case is an offset in doc that exactly matches the
- beginning offset of the parentTag.) It performs the extra work required to keep
- the tag stack in shape and then calls insertHTML(). The editor and doc argu-
- ments are the editor pane and document where the HTML should go. The offset
- argument represents the cursor location or selection start in doc. The insert-
- Element and parentTag arguments are used to calculate the proper number of
- tag pops and pushes before inserting the HTML (via html and addTag, which are
- passed directly to insertHTML()).
- */
- // FIXME: not implemented
+ insertAtBoundry(editor, doc, offset, insertElement,
+ html, parentTag, addTag);
}
/**
@@ -433,8 +420,50 @@ public class HTMLEditorKit
String html, HTML.Tag parentTag,
HTML.Tag addTag)
{
- insertAtBoundary(editor, doc, offset, insertElement,
- html, parentTag, addTag);
+ Element parent = insertElement;
+ Element el;
+ // Find common parent element.
+ if (offset > 0 || insertElement == null)
+ {
+ el = doc.getDefaultRootElement();
+ while (el != null && el.getStartOffset() != offset
+ && ! el.isLeaf())
+ el = el.getElement(el.getElementIndex(offset));
+ parent = el != null ? el.getParentElement() : null;
+ }
+ if (parent != null)
+ {
+ int pops = 0;
+ int pushes = 0;
+ if (offset == 0 && insertElement != null)
+ {
+ el = parent;
+ while (el != null && ! el.isLeaf())
+ {
+ el = el.getElement(el.getElementIndex(offset));
+ pops++;
+ }
+ }
+ else
+ {
+ el = parent;
+ offset--;
+ while (el != null && ! el.isLeaf())
+ {
+ el = el.getElement(el.getElementIndex(offset));
+ pops++;
+ }
+ el = parent;
+ offset++;
+ while (el != null && el != insertElement)
+ {
+ el = el.getElement(el.getElementIndex(offset));
+ pushes++;
+ }
+ }
+ pops = Math.max(0, pops - 1);
+ insertHTML(editor, doc, offset, html, pops, pushes, addTag);
+ }
}
/**
@@ -444,16 +473,97 @@ public class HTMLEditorKit
*/
public void actionPerformed(ActionEvent ae)
{
- Object source = ae.getSource();
- if (source instanceof JEditorPane)
+ JEditorPane source = getEditor(ae);
+ if (source != null)
+ {
+ HTMLDocument d = getHTMLDocument(source);
+ int offset = source.getSelectionStart();
+ int length = d.getLength();
+ boolean inserted = true;
+ if (! tryInsert(source, d, offset, parentTag, addTag))
+ {
+ inserted = tryInsert(source, d, offset, alternateParentTag,
+ alternateAddTag);
+ }
+ if (inserted)
+ adjustSelection(source, d, offset, length);
+ }
+ }
+
+ /**
+ * Tries to insert the html chunk to the specified <code>addTag</code>.
+ *
+ * @param pane the editor
+ * @param doc the document
+ * @param offset the offset at which to insert
+ * @param tag the tag at which to insert
+ * @param addTag the add tag
+ *
+ * @return <code>true</code> when the html has been inserted successfully,
+ * <code>false</code> otherwise
+ */
+ private boolean tryInsert(JEditorPane pane, HTMLDocument doc, int offset,
+ HTML.Tag tag, HTML.Tag addTag)
+ {
+ boolean inserted = false;
+ Element el = findElementMatchingTag(doc, offset, tag);
+ if (el != null && el.getStartOffset() == offset)
+ {
+ insertAtBoundary(pane, doc, offset, el, html, tag, addTag);
+ inserted = true;
+ }
+ else if (offset > 0)
{
- JEditorPane pane = ((JEditorPane) source);
- Document d = pane.getDocument();
- if (d instanceof HTMLDocument)
- insertHTML(pane, (HTMLDocument) d, 0, html, 0, 0, addTag);
- // FIXME: is this correct parameters?
+ int depth = elementCountToTag(doc, offset - 1, tag);
+ if (depth != -1)
+ {
+ insertHTML(pane, doc, offset, html, depth, 0, addTag);
+ inserted = true;
+ }
+ }
+ return inserted;
+ }
+
+ /**
+ * Adjusts the selection after an insertion has been performed.
+ *
+ * @param pane the editor pane
+ * @param doc the document
+ * @param offset the insert offset
+ * @param oldLen the old document length
+ */
+ private void adjustSelection(JEditorPane pane, HTMLDocument doc,
+ int offset, int oldLen)
+ {
+ int newLen = doc.getLength();
+ if (newLen != oldLen && offset < newLen)
+ {
+ if (offset > 0)
+ {
+ String text;
+ try
+ {
+ text = doc.getText(offset - 1, 1);
+ }
+ catch (BadLocationException ex)
+ {
+ text = null;
+ }
+ if (text != null && text.length() > 0
+ && text.charAt(0) == '\n')
+ {
+ pane.select(offset, offset);
+ }
+ else
+ {
+ pane.select(offset + 1, offset + 1);
+ }
+ }
+ else
+ {
+ pane.select(1, 1);
+ }
}
- // FIXME: else not implemented
}
}
@@ -885,8 +995,36 @@ public class HTMLEditorKit
/**
* Actions for HTML
*/
- private static final Action[] defaultActions = {
- // FIXME: Add default actions for html
+ private static final Action[] defaultActions =
+ {
+ new InsertHTMLTextAction("InsertTable",
+ "<table border=1><tr><td></td></tr></table>",
+ HTML.Tag.BODY, HTML.Tag.TABLE),
+ new InsertHTMLTextAction("InsertTableRow",
+ "<table border=1><tr><td></td></tr></table>",
+ HTML.Tag.TABLE, HTML.Tag.TR,
+ HTML.Tag.BODY, HTML.Tag.TABLE),
+ new InsertHTMLTextAction("InsertTableCell",
+ "<table border=1><tr><td></td></tr></table>",
+ HTML.Tag.TR, HTML.Tag.TD,
+ HTML.Tag.BODY, HTML.Tag.TABLE),
+ new InsertHTMLTextAction("InsertUnorderedList",
+ "<ul><li></li></ul>",
+ HTML.Tag.BODY, HTML.Tag.UL),
+ new InsertHTMLTextAction("InsertUnorderedListItem",
+ "<ul><li></li></ul>",
+ HTML.Tag.UL, HTML.Tag.LI,
+ HTML.Tag.BODY, HTML.Tag.UL),
+ new InsertHTMLTextAction("InsertOrderedList",
+ "<ol><li></li></ol>",
+ HTML.Tag.BODY, HTML.Tag.OL),
+ new InsertHTMLTextAction("InsertOrderedListItem",
+ "<ol><li></li></ol>",
+ HTML.Tag.OL, HTML.Tag.LI,
+ HTML.Tag.BODY, HTML.Tag.OL),
+ new InsertHTMLTextAction("InsertPre",
+ "<pre></pre>", HTML.Tag.BODY, HTML.Tag.PRE)
+ // TODO: The reference impl has an InsertHRAction too.
};
/**
@@ -956,8 +1094,15 @@ public class HTMLEditorKit
*/
public Document createDefaultDocument()
{
- HTMLDocument document = new HTMLDocument(getStyleSheet());
+ // Protect the shared stylesheet.
+ StyleSheet styleSheet = getStyleSheet();
+ StyleSheet ss = new StyleSheet();
+ ss.addStyleSheet(styleSheet);
+
+ HTMLDocument document = new HTMLDocument(ss);
document.setParser(getParser());
+ document.setAsynchronousLoadPriority(4);
+ document.setTokenThreshold(100);
return document;
}
diff --git a/javax/swing/text/html/ImageView.java b/javax/swing/text/html/ImageView.java
index ff0d3ea40..f073c6d05 100644
--- a/javax/swing/text/html/ImageView.java
+++ b/javax/swing/text/html/ImageView.java
@@ -2,17 +2,19 @@ package javax.swing.text.html;
import gnu.javax.swing.text.html.CombinedAttributes;
import gnu.javax.swing.text.html.ImageViewIconFactory;
+import gnu.javax.swing.text.html.css.Length;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Rectangle;
import java.awt.Shape;
+import java.awt.Toolkit;
+import java.awt.image.ImageObserver;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.Icon;
-import javax.swing.ImageIcon;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
@@ -29,15 +31,39 @@ import javax.swing.text.html.HTML.Attribute;
public class ImageView extends View
{
/**
+ * Tracks image loading state and performs the necessary layout updates.
+ */
+ class Observer
+ implements ImageObserver
+ {
+
+ public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height)
+ {
+ boolean widthChanged = false;
+ if ((flags & ImageObserver.WIDTH) != 0
+ && ! getElement().getAttributes().isDefined(HTML.Attribute.WIDTH))
+ widthChanged = true;
+ boolean heightChanged = false;
+ if ((flags & ImageObserver.HEIGHT) != 0
+ && ! getElement().getAttributes().isDefined(HTML.Attribute.HEIGHT))
+ widthChanged = true;
+ if (widthChanged || heightChanged)
+ preferenceChanged(ImageView.this, widthChanged, heightChanged);
+ return (flags & ALLBITS) != 0;
+ }
+
+ }
+
+ /**
* True if the image loads synchronuosly (on demand). By default, the image
* loads asynchronuosly.
*/
boolean loadOnDemand;
-
+
/**
* The image icon, wrapping the image,
*/
- ImageIcon imageIcon;
+ Image image;
/**
* The image state.
@@ -45,6 +71,46 @@ public class ImageView extends View
byte imageState = MediaTracker.LOADING;
/**
+ * True when the image needs re-loading, false otherwise.
+ */
+ private boolean reloadImage;
+
+ /**
+ * True when the image properties need re-loading, false otherwise.
+ */
+ private boolean reloadProperties;
+
+ /**
+ * True when the width is set as CSS/HTML attribute.
+ */
+ private boolean haveWidth;
+
+ /**
+ * True when the height is set as CSS/HTML attribute.
+ */
+ private boolean haveHeight;
+
+ /**
+ * True when the image is currently loading.
+ */
+ private boolean loading;
+
+ /**
+ * The current width of the image.
+ */
+ private int width;
+
+ /**
+ * The current height of the image.
+ */
+ private int height;
+
+ /**
+ * Our ImageObserver for tracking the loading state.
+ */
+ private ImageObserver observer;
+
+ /**
* Creates the image view that represents the given element.
*
* @param element the element, represented by this image view.
@@ -52,25 +118,34 @@ public class ImageView extends View
public ImageView(Element element)
{
super(element);
+ observer = new Observer();
+ reloadProperties = true;
+ reloadImage = true;
}
/**
* Load or reload the image. This method initiates the image reloading. After
* the image is ready, the repaint event will be scheduled. The current image,
* if it already exists, will be discarded.
- *
- * @param itsTime
- * also load if the "on demand" property is set
*/
- void reloadImage(boolean itsTime)
+ private void reloadImage()
{
- URL url = getImageURL();
- if (url == null)
- imageState = (byte) MediaTracker.ERRORED;
- else if (!(loadOnDemand && !itsTime))
- imageIcon = new ImageIcon(url);
- else
- imageState = (byte) MediaTracker.LOADING;
+ loading = true;
+ reloadImage = false;
+ haveWidth = false;
+ haveHeight = false;
+ image = null;
+ width = 0;
+ height = 0;
+ try
+ {
+ loadImage();
+ updateSize();
+ }
+ finally
+ {
+ loading = false;
+ }
}
/**
@@ -159,10 +234,8 @@ public class ImageView extends View
*/
public Image getImage()
{
- if (imageIcon == null)
- return null;
- else
- return imageIcon.getImage();
+ updateState();
+ return image;
}
/**
@@ -245,9 +318,9 @@ public class ImageView extends View
if (axis == View.X_AXIS)
{
- Object w = attrs.getAttribute(Attribute.WIDTH);
- if (w != null)
- return Integer.parseInt(w.toString());
+ Object w = attrs.getAttribute(CSS.Attribute.WIDTH);
+ if (w instanceof Length)
+ return ((Length) w).getValue();
else if (image != null)
return image.getWidth(getContainer());
else
@@ -255,9 +328,9 @@ public class ImageView extends View
}
else if (axis == View.Y_AXIS)
{
- Object w = attrs.getAttribute(Attribute.HEIGHT);
- if (w != null)
- return Integer.parseInt(w.toString());
+ Object w = attrs.getAttribute(CSS.Attribute.HEIGHT);
+ if (w instanceof Length)
+ return ((Length) w).getValue();
else if (image != null)
return image.getHeight(getContainer());
else
@@ -291,7 +364,7 @@ public class ImageView extends View
{
return getAltText();
}
-
+
/**
* Paints the image or one of the two image state icons. The image is resized
* to the shape bounds. If there is no image available, the alternative text
@@ -305,83 +378,22 @@ public class ImageView extends View
*/
public void paint(Graphics g, Shape bounds)
{
- Rectangle r = bounds.getBounds();
-
- if (imageIcon == null)
-
- {
- // Loading image on demand, rendering the loading icon so far.
- reloadImage(true);
-
- // The reloadImage sets the imageIcon, unless the URL is broken
- // or malformed.
- if (imageIcon != null)
- {
- if (imageIcon.getImageLoadStatus() != MediaTracker.COMPLETE)
- {
- // Render "not ready" icon, unless the image is ready
- // immediately.
- renderIcon(g, r, getLoadingImageIcon());
- // Add the listener to repaint when the icon will be ready.
- imageIcon.setImageObserver(getContainer());
- return;
- }
- }
- else
- {
- renderIcon(g, r, getNoImageIcon());
- return;
- }
- }
-
- imageState = (byte) imageIcon.getImageLoadStatus();
-
- switch (imageState)
- {
- case MediaTracker.ABORTED:
- case MediaTracker.ERRORED:
- renderIcon(g, r, getNoImageIcon());
- break;
- case MediaTracker.LOADING:
- // If the image is not loaded completely, we still render it, as the
- // partial image may be available.
- case MediaTracker.COMPLETE:
+ updateState();
+ Rectangle r = bounds instanceof Rectangle ? (Rectangle) bounds
+ : bounds.getBounds();
+ Image image = getImage();
+ if (image != null)
{
- // Paint the scaled image.
- Image scaled = imageIcon.getImage().getScaledInstance(
- r.width,
- r.height,
- Image.SCALE_DEFAULT);
- ImageIcon painter = new ImageIcon(scaled);
- painter.paintIcon(getContainer(), g, r.x, r.y);
+ g.drawImage(image, r.x, r.y, r.width, r.height, observer);
}
- break;
- }
- }
-
- /**
- * Render "no image" icon and the alternative "no image" text. The text is
- * rendered right from the icon and is aligned to the icon bottom.
- */
- private void renderIcon(Graphics g, Rectangle bounds, Icon icon)
- {
- Shape current = g.getClip();
- try
+ else
{
- g.setClip(bounds);
+ Icon icon = getNoImageIcon();
if (icon != null)
- {
- icon.paintIcon(getContainer(), g, bounds.x, bounds.y);
- g.drawString(getAltText(), bounds.x + icon.getIconWidth(),
- bounds.y + icon.getIconHeight());
- }
- }
- finally
- {
- g.setClip(current);
+ icon.paintIcon(getContainer(), g, r.x, r.y);
}
}
-
+
/**
* Set if the image should be loaded only when needed (synchronuosly). By
* default, the image loads asynchronuosly. If the image is not yet ready, the
@@ -398,9 +410,7 @@ public class ImageView extends View
*/
protected void setPropertiesFromAttributes()
{
- // In the current implementation, nothing is cached yet, unless the image
- // itself.
- imageIcon = null;
+ // FIXME: Implement this properly.
}
/**
@@ -436,9 +446,90 @@ public class ImageView extends View
*/
public void setSize(float width, float height)
{
- if (imageIcon == null)
- reloadImage(false);
+ updateState();
+ // TODO: Implement this when we have an alt view for the alt=... attribute.
}
-
+ /**
+ * This makes sure that the image and properties have been loaded.
+ */
+ private void updateState()
+ {
+ if (reloadImage)
+ reloadImage();
+ if (reloadProperties)
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Actually loads the image.
+ */
+ private void loadImage()
+ {
+ URL src = getImageURL();
+ Image newImage = null;
+ if (src != null)
+ {
+ // Call getImage(URL) to allow the toolkit caching of that image URL.
+ newImage = Toolkit.getDefaultToolkit().getImage(src);
+ if (newImage != null && getLoadsSynchronously())
+ {
+ // Load image synchronously.
+ MediaTracker tracker = new MediaTracker(getContainer());
+ tracker.addImage(newImage, 0);
+ try
+ {
+ tracker.waitForID(0);
+ }
+ catch (InterruptedException ex)
+ {
+ Thread.interrupted();
+ }
+
+ }
+ }
+ image = newImage;
+ }
+
+ /**
+ * Updates the size parameters of the image.
+ */
+ private void updateSize()
+ {
+ int newW = 0;
+ int newH = 0;
+ Image newIm = getImage();
+ if (newIm != null)
+ {
+ AttributeSet atts = getAttributes();
+ // Fetch width.
+ Length l = (Length) atts.getAttribute(CSS.Attribute.WIDTH);
+ if (l != null)
+ {
+ newW = (int) l.getValue();
+ haveWidth = true;
+ }
+ else
+ {
+ newW = newIm.getWidth(observer);
+ }
+ // Fetch height.
+ l = (Length) atts.getAttribute(CSS.Attribute.HEIGHT);
+ if (l != null)
+ {
+ newH = (int) l.getValue();
+ haveHeight = true;
+ }
+ else
+ {
+ newW = newIm.getWidth(observer);
+ }
+ // Go and trigger loading.
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ if (haveWidth || haveHeight)
+ tk.prepareImage(newIm, width, height, observer);
+ else
+ tk.prepareImage(newIm, -1, -1, observer);
+ }
+ }
}
diff --git a/javax/swing/text/html/ParagraphView.java b/javax/swing/text/html/ParagraphView.java
index e3f2817be..8443515d3 100644
--- a/javax/swing/text/html/ParagraphView.java
+++ b/javax/swing/text/html/ParagraphView.java
@@ -187,28 +187,14 @@ public class ParagraphView
SizeRequirements r)
{
r = super.calculateMinorAxisRequirements(axis, r);
- if (setCSSSpan(r, axis))
+ if (! setCSSSpan(r, axis))
{
- // If we have set the span from CSS, then we need to adjust
- // the margins.
- SizeRequirements parent = super.calculateMinorAxisRequirements(axis,
- null);
int margin = axis == X_AXIS ? getLeftInset() + getRightInset()
: getTopInset() + getBottomInset();
r.minimum -= margin;
r.preferred -= margin;
r.maximum -= margin;
}
- else
- {
- float min = 0;
- int n = getLayoutViewCount();
- for (int i = 0; i < n; i++)
- min = Math.max(getLayoutView(i).getMinimumSpan(axis), min);
- r.minimum = (int) min;
- r.preferred = Math.max(r.preferred, r.minimum);
- r.maximum = Math.max(r.maximum, r.preferred);
- }
return r;
}
diff --git a/javax/swing/text/html/StyleSheet.java b/javax/swing/text/html/StyleSheet.java
index add22e01c..3322a390c 100644
--- a/javax/swing/text/html/StyleSheet.java
+++ b/javax/swing/text/html/StyleSheet.java
@@ -50,6 +50,8 @@ import gnu.javax.swing.text.html.css.Selector;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -107,9 +109,9 @@ public class StyleSheet extends StyleContext
implements CSSParserCallback
{
/**
- * The current style.
+ * The current styles.
*/
- private CSSStyle style;
+ private CSSStyle[] styles;
/**
* The precedence of the stylesheet to be parsed.
@@ -133,9 +135,11 @@ public class StyleSheet extends StyleContext
*
* @param sel the selector
*/
- public void startStatement(Selector sel)
+ public void startStatement(Selector[] sel)
{
- style = new CSSStyle(precedence, sel);
+ styles = new CSSStyle[sel.length];
+ for (int i = 0; i < sel.length; i++)
+ styles[i] = new CSSStyle(precedence, sel[i]);
}
/**
@@ -143,8 +147,9 @@ public class StyleSheet extends StyleContext
*/
public void endStatement()
{
- css.add(style);
- style = null;
+ for (int i = 0; i < styles.length; i++)
+ css.add(styles[i]);
+ styles = null;
}
/**
@@ -157,9 +162,13 @@ public class StyleSheet extends StyleContext
{
CSS.Attribute cssAtt = CSS.getAttribute(property);
Object val = CSS.getValue(cssAtt, value);
- CSS.addInternal(style, cssAtt, value);
- if (cssAtt != null)
- style.addAttribute(cssAtt, val);
+ for (int i = 0; i < styles.length; i++)
+ {
+ CSSStyle style = styles[i];
+ CSS.addInternal(style, cssAtt, value);
+ if (cssAtt != null)
+ style.addAttribute(cssAtt, val);
+ }
}
}
@@ -172,11 +181,11 @@ public class StyleSheet extends StyleContext
implements Style, Comparable
{
- static final int PREC_UA = 400000;
- static final int PREC_NORM = 300000;
+ static final int PREC_UA = 0;
+ static final int PREC_NORM = 100000;
static final int PREC_AUTHOR_NORMAL = 200000;
- static final int PREC_AUTHOR_IMPORTANT = 100000;
- static final int PREC_USER_IMPORTANT = 0;
+ static final int PREC_AUTHOR_IMPORTANT = 300000;
+ static final int PREC_USER_IMPORTANT = 400000;
/**
* The priority of this style when matching CSS selectors.
@@ -231,8 +240,10 @@ public class StyleSheet extends StyleContext
/** Base font size (int) */
int baseFontSize;
- /** The style sheets stored. */
- StyleSheet[] styleSheet;
+ /**
+ * The linked style sheets stored.
+ */
+ private ArrayList linked;
/**
* Maps element names (selectors) to AttributSet (the corresponding style
@@ -424,6 +435,21 @@ public class StyleSheet extends StyleContext
styles.add(style);
}
+ // Add styles from linked stylesheets.
+ if (linked != null)
+ {
+ for (int i = linked.size() - 1; i >= 0; i--)
+ {
+ StyleSheet ss = (StyleSheet) linked.get(i);
+ for (int j = ss.css.size() - 1; j >= 0; j--)
+ {
+ CSSStyle style = (CSSStyle) ss.css.get(j);
+ if (style.selector.matches(tags, classes, ids))
+ styles.add(style);
+ }
+ }
+ }
+
// Sort selectors.
Collections.sort(styles);
Style[] styleArray = new Style[styles.size()];
@@ -444,7 +470,6 @@ public class StyleSheet extends StyleContext
*/
public Style getRule(String selector)
{
- Selector sel = new Selector(selector);
CSSStyle best = null;
for (Iterator i = css.iterator(); i.hasNext();)
{
@@ -477,6 +502,9 @@ public class StyleSheet extends StyleContext
// Shouldn't happen. And if, then we
System.err.println("IOException while parsing stylesheet: " + ex.getMessage());
}
+ // Clean up resolved styles cache so that the new styles are recognized
+ // on next stylesheet request.
+ resolvedStyles.clear();
}
/**
@@ -546,11 +574,9 @@ public class StyleSheet extends StyleContext
*/
public void addStyleSheet(StyleSheet ss)
{
- if (styleSheet == null)
- styleSheet = new StyleSheet[] {ss};
- else
- System.arraycopy(new StyleSheet[] {ss}, 0, styleSheet,
- styleSheet.length, 1);
+ if (linked == null)
+ linked = new ArrayList();
+ linked.add(ss);
}
/**
@@ -560,31 +586,9 @@ public class StyleSheet extends StyleContext
*/
public void removeStyleSheet(StyleSheet ss)
{
- if (styleSheet.length == 1 && styleSheet[0].equals(ss))
- styleSheet = null;
- else
+ if (linked != null)
{
- for (int i = 0; i < styleSheet.length; i++)
- {
- StyleSheet curr = styleSheet[i];
- if (curr.equals(ss))
- {
- StyleSheet[] tmp = new StyleSheet[styleSheet.length - 1];
- if (i != 0 && i != (styleSheet.length - 1))
- {
- System.arraycopy(styleSheet, 0, tmp, 0, i);
- System.arraycopy(styleSheet, i + 1, tmp, i,
- styleSheet.length - i - 1);
- }
- else if (i == 0)
- System.arraycopy(styleSheet, 1, tmp, 0, styleSheet.length - 1);
- else
- System.arraycopy(styleSheet, 0, tmp, 0, styleSheet.length - 1);
-
- styleSheet = tmp;
- break;
- }
- }
+ linked.remove(ss);
}
}
@@ -595,7 +599,17 @@ public class StyleSheet extends StyleContext
*/
public StyleSheet[] getStyleSheets()
{
- return styleSheet;
+ StyleSheet[] linkedSS;
+ if (linked != null)
+ {
+ linkedSS = new StyleSheet[linked.size()];
+ linkedSS = (StyleSheet[]) linked.toArray(linkedSS);
+ }
+ else
+ {
+ linkedSS = null;
+ }
+ return linkedSS;
}
/**
@@ -697,18 +711,39 @@ public class StyleSheet extends StyleContext
o = htmlAttrSet.getAttribute(HTML.Attribute.WIDTH);
if (o != null)
cssAttr = addAttribute(cssAttr, CSS.Attribute.WIDTH,
- CSS.getValue(CSS.Attribute.WIDTH, o.toString()));
+ new Length(o.toString()));
// The HTML height attribute maps directly to CSS height.
o = htmlAttrSet.getAttribute(HTML.Attribute.HEIGHT);
if (o != null)
cssAttr = addAttribute(cssAttr, CSS.Attribute.HEIGHT,
- CSS.getValue(CSS.Attribute.HEIGHT, o.toString()));
+ new Length(o.toString()));
o = htmlAttrSet.getAttribute(HTML.Attribute.NOWRAP);
if (o != null)
cssAttr = addAttribute(cssAttr, CSS.Attribute.WHITE_SPACE, "nowrap");
+ // Map cellspacing attr of tables to CSS border-spacing.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.CELLSPACING);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_SPACING,
+ new Length(o.toString()));
+
+ // For table cells and headers, fetch the cellpadding value from the
+ // parent table and set it as CSS padding attribute.
+ HTML.Tag tag = (HTML.Tag)
+ htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
+ if ((tag == HTML.Tag.TD || tag == HTML.Tag.TH)
+ && htmlAttrSet instanceof Element)
+ {
+ Element el = (Element) htmlAttrSet;
+ AttributeSet tableAttrs = el.getParentElement().getParentElement()
+ .getAttributes();
+ o = tableAttrs.getAttribute(HTML.Attribute.CELLPADDING);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING,
+ new Length(o.toString()));
+ }
// TODO: Add more mappings.
return cssAttr;
}
@@ -824,10 +859,7 @@ public class StyleSheet extends StyleContext
*/
public Font getFont(AttributeSet a)
{
- FontSize size = (FontSize) a.getAttribute(CSS.Attribute.FONT_SIZE);
- int realSize = 12;
- if (size != null)
- realSize = size.getValue();
+ int realSize = getFontSize(a);
// Decrement size for subscript and superscript.
Object valign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
@@ -852,6 +884,41 @@ public class StyleSheet extends StyleContext
}
/**
+ * Resolves the fontsize for a given set of attributes.
+ *
+ * @param atts the attributes
+ *
+ * @return the resolved font size
+ */
+ private int getFontSize(AttributeSet atts)
+ {
+ int size = 12;
+ if (atts.isDefined(CSS.Attribute.FONT_SIZE))
+ {
+ FontSize fs = (FontSize) atts.getAttribute(CSS.Attribute.FONT_SIZE);
+ if (fs.isRelative())
+ {
+ int parSize = 12;
+ AttributeSet resolver = atts.getResolveParent();
+ if (resolver != null)
+ parSize = getFontSize(resolver);
+ size = fs.getValue(parSize);
+ }
+ else
+ {
+ size = fs.getValue();
+ }
+ }
+ else
+ {
+ AttributeSet resolver = atts.getResolveParent();
+ if (resolver != null)
+ size = getFontSize(resolver);
+ }
+ return size;
+ }
+
+ /**
* Takes a set of attributes and turns it into a foreground
* color specification. This is used to specify things like, brigher, more hue
* etc.
@@ -1036,6 +1103,11 @@ public class StyleSheet extends StyleContext
*/
private Border border;
+ private float leftPadding;
+ private float rightPadding;
+ private float topPadding;
+ private float bottomPadding;
+
/**
* The background color.
*/
@@ -1048,9 +1120,17 @@ public class StyleSheet extends StyleContext
*/
BoxPainter(AttributeSet as, StyleSheet ss)
{
- Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT);
+ // Fetch margins.
+ Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN);
+ if (l != null)
+ {
+ topInset = bottomInset = leftInset = rightInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT);
if (l != null)
leftInset = l.getValue();
+ else if (as.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.UL)
+ System.err.println("UL margin left value: " + l + " atts: " + as);
l = (Length) as.getAttribute(CSS.Attribute.MARGIN_RIGHT);
if (l != null)
rightInset = l.getValue();
@@ -1061,6 +1141,26 @@ public class StyleSheet extends StyleContext
if (l != null)
bottomInset = l.getValue();
+ // Fetch padding.
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING);
+ if (l != null)
+ {
+ leftPadding = rightPadding = topPadding = bottomPadding =
+ l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_LEFT);
+ if (l != null)
+ leftPadding = l.getValue();
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_RIGHT);
+ if (l != null)
+ rightPadding = l.getValue();
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_TOP);
+ if (l != null)
+ topPadding = l.getValue();
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_BOTTOM);
+ if (l != null)
+ bottomPadding = l.getValue();
+
// Determine border.
border = new CSSBorder(as);
@@ -1090,21 +1190,25 @@ public class StyleSheet extends StyleContext
inset = topInset;
if (border != null)
inset += border.getBorderInsets(null).top;
+ inset += topPadding;
break;
case View.BOTTOM:
inset = bottomInset;
if (border != null)
inset += border.getBorderInsets(null).bottom;
+ inset += bottomPadding;
break;
case View.LEFT:
inset = leftInset;
if (border != null)
inset += border.getBorderInsets(null).left;
+ inset += leftPadding;
break;
case View.RIGHT:
inset = rightInset;
if (border != null)
inset += border.getBorderInsets(null).right;
+ inset += rightPadding;
break;
default:
inset = 0.0F;
@@ -1176,6 +1280,11 @@ public class StyleSheet extends StyleContext
}
/**
+ * Cached rectangle re-used in the paint method below.
+ */
+ private final Rectangle tmpRect = new Rectangle();
+
+ /**
* Paints the CSS list decoration according to the attributes given.
*
* @param g - the graphics configuration
@@ -1200,7 +1309,35 @@ public class StyleSheet extends StyleContext
if (tag != null && tag == HTML.Tag.LI)
{
g.setColor(Color.BLACK);
- g.fillOval((int) x - 15, (int) (h / 2 - 3 + y), 6, 6);
+ int centerX = (int) (x - 12);
+ int centerY = -1;
+ // For paragraphs (almost all cases) center bullet vertically
+ // in the middle of the first line.
+ tmpRect.setBounds((int) x, (int) y, (int) w, (int) h);
+ if (itemView.getViewCount() > 0)
+ {
+ View v1 = itemView.getView(0);
+ if (v1 instanceof ParagraphView && v1.getViewCount() > 0)
+ {
+ Shape a1 = itemView.getChildAllocation(0, tmpRect);
+ Rectangle r1 = a1 instanceof Rectangle ? (Rectangle) a1
+ : a1.getBounds();
+ ParagraphView par = (ParagraphView) v1;
+ Shape a = par.getChildAllocation(0, r1);
+ if (a != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a
+ : a.getBounds();
+ centerY = (int) (r.height / 2 + r.y);
+ }
+ }
+ }
+ if (centerY == -1)
+ {
+ System.err.println("WARNING LI child is not a paragraph view " + itemView.getView(0) + ", " + itemView.getViewCount());
+ centerY =(int) (h / 2 + y);
+ }
+ g.fillOval(centerX - 3, centerY - 3, 6, 6);
}
}
}
diff --git a/javax/swing/text/html/TableView.java b/javax/swing/text/html/TableView.java
index 2bd11ffcf..971d54cb6 100644
--- a/javax/swing/text/html/TableView.java
+++ b/javax/swing/text/html/TableView.java
@@ -38,9 +38,12 @@ exception statement from your version. */
package javax.swing.text.html;
+import java.awt.Shape;
+
import gnu.javax.swing.text.html.css.Length;
import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
import javax.swing.text.AttributeSet;
import javax.swing.text.BoxView;
import javax.swing.text.Element;
@@ -75,6 +78,12 @@ class TableView
super(el, X_AXIS);
}
+ public void replace(int offset, int len, View[] views)
+ {
+ super.replace(offset, len, views);
+ gridValid = false;
+ }
+
/**
* Overridden to make rows not resizable along the Y axis.
*/
@@ -84,7 +93,27 @@ class TableView
if (axis == Y_AXIS)
span = super.getPreferredSpan(axis);
else
- span = super.getMaximumSpan(axis);
+ span = Integer.MAX_VALUE;
+ return span;
+ }
+
+ public float getMinimumSpan(int axis)
+ {
+ float span;
+ if (axis == X_AXIS)
+ span = totalColumnRequirements.minimum;
+ else
+ span = super.getMinimumSpan(axis);
+ return span;
+ }
+
+ public float getPreferredSpan(int axis)
+ {
+ float span;
+ if (axis == X_AXIS)
+ span = totalColumnRequirements.preferred;
+ else
+ span = super.getPreferredSpan(axis);
return span;
}
@@ -97,9 +126,10 @@ class TableView
{
if (r == null)
r = new SizeRequirements();
- r.minimum = totalColumnRequirements.minimum;
- r.preferred = totalColumnRequirements.preferred;
- r.maximum = totalColumnRequirements.maximum;
+ int adjust = (columnRequirements.length + 1) * cellSpacing;
+ r.minimum = totalColumnRequirements.minimum + adjust;
+ r.preferred = totalColumnRequirements.preferred + adjust;
+ r.maximum = totalColumnRequirements.maximum + adjust;
r.alignment = 0.0F;
return r;
}
@@ -123,6 +153,8 @@ class TableView
for (int j = 0; j < cv.colSpan; j++, realColumn++)
{
spans[i] += columnSpans[realColumn];
+ if (j < cv.colSpan - 1)
+ spans[i] += cellSpacing;
}
}
}
@@ -151,6 +183,14 @@ class TableView
super(el, Y_AXIS);
}
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ r = super.calculateMajorAxisRequirements(axis, r);
+ r.maximum = Integer.MAX_VALUE;
+ return r;
+ }
+
/**
* Overridden to fetch the columnSpan attibute.
*/
@@ -183,8 +223,10 @@ class TableView
/**
* The column requirements.
+ *
+ * Package private to avoid accessor methods.
*/
- private SizeRequirements[] columnRequirements;
+ SizeRequirements[] columnRequirements;
/**
* The overall requirements across all columns.
@@ -215,7 +257,14 @@ class TableView
/**
* Indicates if the grid setup is ok.
*/
- private boolean gridValid;
+ boolean gridValid;
+
+ /**
+ * Additional space that is added _between_ table cells.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ int cellSpacing;
/**
* Creates a new HTML table view for the specified element.
@@ -318,6 +367,11 @@ class TableView
r.minimum = width;
}
+ // Adjust requirements when we have cell spacing.
+ int adjust = (columnRequirements.length + 1) * cellSpacing;
+ r.minimum += adjust;
+ r.preferred += adjust;
+
// Apply the alignment.
Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN);
r.alignment = 0.0F;
@@ -332,6 +386,8 @@ class TableView
r.alignment = 1.0F;
}
+ // Make it not resize in the horizontal direction.
+ r.maximum = r.preferred;
return r;
}
@@ -343,6 +399,16 @@ class TableView
int[] spans)
{
updateGrid();
+
+ // Mark all rows as invalid.
+ int n = getViewCount();
+ for (int i = 0; i < n; i++)
+ {
+ View row = getView(i);
+ if (row instanceof RowView)
+ ((RowView) row).layoutChanged(axis);
+ }
+
layoutColumns(targetSpan);
super.layoutMinorAxis(targetSpan, axis, offsets, spans);
}
@@ -361,8 +427,12 @@ class TableView
// all columns of all rows.
for (int r = 0; r < numRows; r++)
{
- RowView rowView = (RowView) getView(r);
- int numCols = rowView.getViewCount();
+ View rowView = getView(r);
+ int numCols;
+ if (rowView instanceof RowView)
+ numCols = ((RowView) rowView).getViewCount();
+ else
+ numCols = 0;
// We collect the normal (non-relative) column requirements in the
// total variable and the relative requirements in the relTotal
@@ -533,7 +603,9 @@ class TableView
}
// Try to adjust the spans so that we fill the targetSpan.
- long diff = targetSpan - sumPref;
+ // For adjustments we have to use the targetSpan minus the cumulated
+ // cell spacings.
+ long diff = targetSpan - (n + 1) * cellSpacing - sumPref;
float factor = 0.0F;
int[] diffs = null;
if (diff != 0)
@@ -570,7 +642,7 @@ class TableView
}
// Actually perform adjustments.
- int totalOffs = 0;
+ int totalOffs = cellSpacing;
for (int i = 0; i < n; i++)
{
columnOffsets[i] = totalOffs;
@@ -580,8 +652,8 @@ class TableView
columnSpans[i] += Math.round(adjust);
}
// Avoid overflow here.
- totalOffs = (int) Math.min((long) totalOffs + (long) columnSpans[i],
- Integer.MAX_VALUE);
+ totalOffs = (int) Math.min((long) totalOffs + (long) columnSpans[i]
+ + (long) cellSpacing, Integer.MAX_VALUE);
}
}
@@ -597,15 +669,23 @@ class TableView
int numRows = getViewCount();
for (int r = 0; r < numRows; r++)
{
- RowView rowView = (RowView) getView(r);
- int numCols = rowView.getViewCount();
+ View rowView = getView(r);
+ int numCols;
+ if (rowView instanceof RowView)
+ numCols = ((RowView) rowView).getViewCount();
+ else
+ numCols = 0;
maxColumns = Math.max(numCols, maxColumns);
}
columnWidths = new Length[maxColumns];
for (int r = 0; r < numRows; r++)
{
- RowView rowView = (RowView) getView(r);
- int numCols = rowView.getViewCount();
+ View rowView = getView(r);
+ int numCols;
+ if (rowView instanceof RowView)
+ numCols = ((RowView) rowView).getViewCount();
+ else
+ numCols = 0;
int colIndex = 0;
for (int c = 0; c < numCols; c++)
{
@@ -644,4 +724,86 @@ class TableView
span = super.getMaximumSpan(axis);
return span;
}
+
+ /**
+ * Overridden to fetch the CSS attributes when view gets connected.
+ */
+ public void setParent(View parent)
+ {
+ super.setParent(parent);
+ if (parent != null)
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Fetches CSS and HTML layout attributes.
+ */
+ private void setPropertiesFromAttributes()
+ {
+ // Fetch and parse cell spacing.
+ Object o = getAttributes().getAttribute(CSS.Attribute.BORDER_SPACING);
+ if (o != null && o instanceof Length)
+ {
+ Length l = (Length) o;
+ cellSpacing = (int) l.getValue();
+ }
+ }
+
+ /**
+ * Overridden to adjust for cellSpacing.
+ */
+ protected SizeRequirements calculateMajorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ r = super.calculateMajorAxisRequirements(axis, r);
+ int adjust = (getViewCount() + 1) * cellSpacing;
+ r.minimum += adjust;
+ r.preferred += adjust;
+ r.maximum += adjust;
+ return r;
+ }
+
+ /**
+ * Overridden to adjust for cellSpacing.
+ */
+ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+ int spans[])
+ {
+ int adjust = (getViewCount() + 1) * cellSpacing;
+ super.layoutMajorAxis(targetSpan - adjust, axis, offsets, spans);
+ for (int i = 0; i < offsets.length; i++)
+ {
+ offsets[i] += (i + 1) * cellSpacing;
+ }
+ }
+
+ /**
+ * Overridden to replace view factory with this one.
+ */
+ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.insertUpdate(e, a, this);
+ }
+
+ /**
+ * Overridden to replace view factory with this one.
+ */
+ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.removeUpdate(e, a, this);
+ }
+
+ /**
+ * Overridden to replace view factory with this one.
+ */
+ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.changedUpdate(e, a, this);
+ }
+
+ public void replace(int offset, int len, View[] views)
+ {
+ super.replace(offset, len, views);
+ gridValid = false;
+ }
}